# Dokumentáció
URL: https://www.kasszajs.hu/docs
> A kassza dokumentációja. TypeScript kliens a Számlázz.hu Számla Agenthez, kérés, válasz és XML minta minden művelethez.
## Üdvözlünk a kassza dokumentációjában [#üdvözlünk-a-kassza-dokumentációjában]
**A kassza a Számlázz.hu Számla Agent TypeScript kliense. Számlát, díjbekérőt és nyugtát állít ki, sztornóz, befizetést rögzít, lekéri a PDF-et és a számla adatait, a NAV-tól pedig az adószámhoz tartozó cégadatokat. Nincs futásidejű függősége, így Node.js-en, Bunon, Denón, Cloudflare Workersen és Vercel Edge-en is ugyanúgy fut.**
A Számla Agent műveletein túl is segít: fizetési webhookból pontosan egyszer állít ki bizonylatot, megbízottként más cégek nevében számláz, elkészíti a NAV napi nyugta-összesítőjét, és fogadja a Számlázz.hu pénzügyi adatkapcsolatát.
A dokumentáció a [Számlázz.hu hivatalos Agent dokumentációjának](https://docs.szamlazz.hu/hu/agent/) felépítését követi. Minden műveletnél három oldalt találsz: a **kérést** (mit adsz át a kasszának), a **választ** (mit kapsz vissza, és milyen hibák jöhetnek), és a **mintát** a kóddal, a ténylegesen elküldött XML-lel és a Számlázz.hu válaszával. A hivatalos leírás bármelyik oldala mellé így odateheted a megfelelő kassza oldalt.
Ha kérdésed van, vagy hibát találtál, [nyiss egy issue-t a GitHubon](https://github.com/futozs/kassza/issues).
A kassza független, nyílt forráskódú projekt, és nem a KBOSS.hu Kft. (Számlázz.hu) terméke. Az
Agent kulcsot, az előfizetést és a számlázási beállításokat a Számlázz.hu felületén kezeled.
## Hol kezdjem? [#hol-kezdjem]
Mi történik a kódod és a vevő postafiókja között, amikor kiállítasz egy számlát.
Telepítés, Agent kulcs, és egy működő számla néhány sorból.
Ki adhat számítógéppel előállított nyugtát, és mikor kell mégis számla.
A Számlázz.hu összes ismert hibakódja, kategóriával és magyar javítási tippel.
Dupla számla és kitiltás nélkül, webhookból és várólistából is.
Futtasd a példákat a böngészőben Agent kulcs nélkül, és nézd meg az elküldött XML-t.
## A 11 Számla Agent művelet [#a-11-számla-agent-művelet]
| Művelet | kassza metódus | Hivatalos leírás |
| --- | --- | --- |
| [Számla létrehozás](/docs/szamla-letrehozas) | `invoices.create()`, `invoices.preview()` | [Számla létrehozás](https://docs.szamlazz.hu/hu/agent/generating_invoice/request) |
| [Számla sztornó](/docs/szamla-sztorno) | `invoices.reverse()` | [Számla sztornó](https://docs.szamlazz.hu/hu/agent/reversing_invoice/request) |
| [Befizetés rögzítése](/docs/befizetes-rogzitese) | `invoices.registerPayment()`, `invoices.clearPayments()` | [Befizetés rögzítése](https://docs.szamlazz.hu/hu/agent/credit_entry/request) |
| [Bizonylat lekérése PDF-ben](/docs/bizonylat-pdf) | `invoices.getPdf()` | [PDF lekérés](https://docs.szamlazz.hu/hu/agent/querying_pdf/request) |
| [Számla adatainak lekérése](/docs/szamla-adatai) | `invoices.get()`, `invoices.find()` | [XML lekérés](https://docs.szamlazz.hu/hu/agent/querying_xml/request) |
| [Díjbekérő törlése](/docs/dijbekero-torlese) | `invoices.deleteProforma()` | [Díjbekérő törlése](https://docs.szamlazz.hu/hu/agent/deleting_pro_forma_invoice/request) |
| [Nyugta létrehozás](/docs/nyugta-letrehozas) | `receipts.create()` | [Nyugta létrehozás](https://docs.szamlazz.hu/hu/agent/generating_receipt/request) |
| [Nyugta sztornó](/docs/nyugta-sztorno) | `receipts.reverse()` | [Nyugta sztornó](https://docs.szamlazz.hu/hu/agent/reversing_receipt/request) |
| [Nyugta lekérdezés](/docs/nyugta-lekerdezes) | `receipts.get()`, `receipts.find()` | [Nyugta lekérdezés](https://docs.szamlazz.hu/hu/agent/querying_receipt/request) |
| [Nyugta kiküldés](/docs/nyugta-kikuldes) | `receipts.send()` | [Nyugta kiküldés](https://docs.szamlazz.hu/hu/agent/sending_receipt/request) |
| [Adószám lekérdezése](/docs/adoszam-lekerdezes) | `taxpayer.query()` | [Adószám lekérdezés](https://docs.szamlazz.hu/hu/agent/querying_taxpayer/request) |
## További modulok [#további-modulok]
| Terület | Modul | Mire jó |
| --- | --- | --- |
| [Fizetésből bizonylat](/docs/fizetesek) | `kassza/payments` | Stripe, SimplePay, Barion, Revolut és PayPal fizetésből nyugta vagy számla, aláírás-ellenőrzéssel, pontosan egyszer, visszatérítéskor sztornóval. |
| [NAV nyugta-adatszolgáltatás](/docs/nav-nyugta) | `kassza/reports`, `kassza/nav` | A 2026. szeptember 1-jétől kötelező napi összesítő, egyeztetés a NAV-val, és a kézi tartalék nyugtatömb beküldése. |
| [Megbízotti számlázás](/docs/megbizott-szamlazas) | `kassza/delegation` | Platformoknak: a megbízói fiók csatlakoztatása (az Agent `action-agent_ceg_mb` hívása), majd kiállítás sok cég nevében, a saját Számlázz.hu fiókjukban. |
| [Pénzügyi adatkapcsolat](/docs/adatkapcsolat) | `kassza/data-link` | A Számlázz.hu által átküldött számlák, banki tranzakciók és nyugták fogadása egy könyvelő- vagy ERP-rendszerben. |
| [Hamis Számla Agent](/docs/kiegeszitok/hamis-agent) | `kassza/testing` | Tesztek a valódi kassza kódon, elveszett válasszal, karbantartással és részleges sikerrel. |
| [Parancssor](/docs/kiegeszitok/parancssor) | `npx kassza` | Környezetellenőrzés, a küldendő XML előnézete supportjegyhez, lekérdezések. |
| [MCP szerver](/docs/kiegeszitok/mcp-szerver) | `npx kassza mcp` | Számla és nyugta AI asszisztensből, előnézettel és megerősítéssel. |
# Mi a kassza?
URL: https://www.kasszajs.hu/docs/alapok/mi-a-kassza
> A kassza TypeScript kliens a Számlázz.hu Számla Agenthez. Mit old meg a nyers Agent API-hoz képest, és mit nem.
A **Számla Agent** a Számlázz.hu API-ja: XML fájlokat fogad HTTP POST kérésben, és XML-lel, PDF-fel vagy szöveggel válaszol. A **kassza** ezt az API-t teszi kényelmesen és biztonságosan használhatóvá TypeScriptből. Te objektumokat adsz át és típusos eredményt kapsz vissza, az XML-lel, a kerekítéssel és a hibaüzenetek értelmezésével a kassza foglalkozik.
```ts
import { createKassza } from 'kassza'
const kassza = createKassza()
const szamla = await kassza.invoices.create({
orderNumber: 'REND-1001',
buyer: { name: 'Vevő Kft.', zip: '1111', city: 'Budapest', address: 'Fő utca 1.' },
items: [{ name: 'Tanácsadás', netUnitPrice: 20_000, vat: 27 }],
})
```
## Mit csinál helyetted a kassza? [#mit-csinál-helyetted-a-kassza]
| Feladat a nyers Agent API-val | A kasszában |
| --- | --- |
| XML összeállítása az XSD által előírt, kötött elemsorrendben | Objektumot adsz át, az XML sorrendhelyes lesz |
| Nettó, áfa és bruttó kiszámolása és kerekítése tételenként | `netUnitPrice` vagy `grossUnitPrice`, a kerekítés a hivatalos szabályokat követi |
| Dátum megadása magyar idő szerint | Alapból a mai nap `Europe/Budapest` szerint |
| `multipart/form-data` kérés és mellékletek | `attachments` tömb |
| Session cookie mentése és visszaküldése | Automatikus, serverless környezethez közös tárolóval |
| Hibák kiolvasása fejlécből, XML-ből vagy `[ERR]` szövegből | Egységes `SzamlazzError` kóddal, kategóriával és magyar tippel |
| Újrapróbálás úgy, hogy ne legyen dupla számla | Csak biztonságos műveleteket próbál újra, számlát soha; a `createOnce()` bizonytalan hiba után visszakeres |
## A 11 művelet [#a-11-művelet]
A kassza mind a 11 Számla Agent műveletet lefedi: számla (díjbekérő, előleg-, vég- és helyesbítő számla, szállítólevél) és előnézet, sztornó, befizetés rögzítése, PDF és számlaadatok lekérése, díjbekérő törlése, nyugta létrehozása, sztornója, lekérdezése és kiküldése, valamint az adószám lekérdezése a NAV adatbázisából. A teljes lista a [dokumentáció kezdőlapján](/docs#a-11-számla-agent-művelet) található. A megbízott számlakibocsátáshoz tartozó Agent hívást, a megbízói fiók csatlakoztatását a `kassza/delegation` modul végzi.
## Kiegészítő modulok [#kiegészítő-modulok]
A fő csomag mellett külön importálható modulok is vannak, amelyek csak akkor kerülnek a bundle-be, ha használod őket:
| Modul | Mire jó |
| --- | --- |
| `kassza/testing` | Mock kliens unit tesztekhez, valódi validációval és kerekítéssel, és hamis Számla Agent integrációs tesztekhez |
| `kassza/payments` | Stripe, SimplePay, Barion, Revolut és PayPal webhookból nyugta vagy számla, pontosan egyszer |
| `kassza/reports`, `kassza/nav` | A NAV napi nyugta-összesítője, egyeztetés és a NAV nyugta-adatszolgáltatási interfész kliense |
| `kassza/delegation` | Megbízotti számlázás: megbízói fiók csatlakoztatása és kliensek megbízónként |
| `kassza/data-link` | A Számlázz.hu pénzügyi adatkapcsolatának fogadása könyvelő- és ERP-rendszerben |
| `kassza/mcp` | MCP szerver AI asszisztensekhez; parancssorból `npx kassza mcp` |
| `kassza/ipn` | A Számlázz.hu fizetési értesítésének (IPN) feldolgozása |
| `kassza/storage` | PDF mentése S3-ra, R2-re, Vercel Blobra, UploadThingre vagy Supabase-re |
| `kassza/cookie-stores` | Közös session tároló Redishez és Cloudflare KV-hez |
| `kassza/validators` | Adószám, bankszámla, IBAN, EU adószám és magyar cím ellenőrzése |
| `kassza/money` | A kasszában használt kerekítés és összegzés külön is |
## Hol fut? [#hol-fut]
A kassza nulla futásidejű függőséggel készül, és csak a szabványos `fetch`, `FormData`, `Blob`, `TextEncoder` és `crypto` API-kat használja. Ezért ugyanaz a kód fut Node.js 22-n vagy újabbon, Bunon, Denón, Cloudflare Workersen és a Vercel Edge Runtime-on.
A kassza az Agent kulcsoddal hitelesít, ezért soha ne használd böngészőben futó kódban. A
kulcsot a szerveren tartsd, és a böngészőből a saját API-dat hívd.
## Mit nem csinál a kassza? [#mit-nem-csinál-a-kassza]
- **Nem helyettesíti a Számlázz.hu fiókot.** Az előfizetés, a cégadatok, a számlaszám-előtagok és a számlázási beállítások a Számlázz.hu felületén maradnak.
- **Nem ad adótanácsot.** Az áfakulcs, a bizonylattípus és a devizás számlázás szabályait könyvelőddel egyeztesd.
- **Nem hivatalos termék.** A kassza független, nyílt forráskódú projekt, a Számlázz.hu (KBOSS.hu Kft.) nem támogatja és nem felel érte.
# Hogyan működik a kassza?
URL: https://www.kasszajs.hu/docs/alapok/hogyan-mukodik
> Egy számla útja a kódodtól a vevő postafiókjáig, lépésről lépésre, a kassza szemszögéből.
Egy webshopban tipikusan így születik a számla: a vevő fizet, a rendszered meghívja a kasszát, a kassza elküldi a kérést a Számlázz.hu Számla Agentnek, az elkészült számla PDF-je visszajön, a vevő pedig e-mailben is megkapja.
## Mi történik egy hívás alatt? [#mi-történik-egy-hívás-alatt]
1. **Validáció.** A kassza még a kérés előtt ellenőrzi a bemenetet: kötelező mezők, ismert áfakulcs, érvényes dátum, a mellékletek száma és mérete. Ha valami hibás, `validation` kategóriájú `SzamlazzError` jön, és kérés el sem megy.
2. **Kerekítés.** Minden tételnél kiszámolja a nettó, áfa és bruttó értéket a [hivatalos kerekítési szabályok](/docs/szamla-letrehozas/beallitasok-es-szabalyok/kerekites) szerint, így a Számlázz.hu nem utasítja el a számlát 259–264-es hibával.
3. **XML összeállítása.** A kassza saját, sorrendtartó XML-írót használ, mert az Agent XSD-je kötött elemsorrendet követel. Számlánál az Agent kulcs a `` blokkba kerül.
4. **Küldés.** `multipart/form-data` POST kérés megy a `https://www.szamlazz.hu/szamla/` címre, a művelethez tartozó form mezőben. Ha van tárolt session cookie, a kassza visszaküldi.
5. **Válasz értelmezése.** A Számlázz.hu hibát jelezhet fejlécben, XML-ben vagy `[ERR]` szövegben. A kassza mindhármat felismeri, és kódot, kategóriát, magyar tippet ad.
6. **Eredmény.** Sikernél típusos objektumot kapsz, például `CreatedInvoice`-t számlaszámmal, összegekkel és a PDF-fel.
7. **Értesítés.** Ha a vevőnek van e-mail címe, a Számlázz.hu elküldi neki a számlát. Ezt a `buyer.sendEmail: false` kapcsolja ki.
Az egész folyamatot végignézheted a [sandboxban](/sandbox): a Hálózat fülön a ténylegesen
elküldött XML-t és a szimulált Számlázz.hu választ is látod, fejlécekkel és session cookie-val.
## Újrapróbálás és dupla számla [#újrapróbálás-és-dupla-számla]
A számla létrehozása nem idempotens: ha egy kérés kétszer ér be, két számla készül. Ezért a kassza a számlakészítést **soha nem küldi újra magától**, a nyugtát pedig csak akkor, ha `callId` védi a duplikációtól. A lekérdezéseket (PDF, számlaadatok, nyugta, adószám) hálózati hiba, időtúllépés vagy karbantartás esetén alapból háromszor próbálja meg, egyre hosszabb várakozással. A próbálkozások száma a `maxAttempts` opcióval legfeljebb 5-re emelhető, mert a Számlázz.hu ennél többet nem enged egy kérésre.
Ha egy számlakészítés bizonytalan hibával ér véget, például megszakadt a kapcsolat, a helyes lépés a rendelésszám alapján lekérdezni, hogy elkészült-e:
```ts
const letezik = await kassza.invoices.find({ orderNumber: 'REND-1001' })
```
A részleteket a [Hibakezelés, hibakódok](/docs/alapok/hibakezeles) oldal írja le.
## Következő lépések [#következő-lépések]
A csomag telepítése és az első számla.
Form mezők, válaszformátumok, egyedi fetch.
# Nyugta vagy számla?
URL: https://www.kasszajs.hu/docs/alapok/nyugta-vagy-szamla
> Mikor adható Számla Agent nyugta, mikor kell számla, ki online pénztárgép-köteles, és hogyan dönt a kassza chooseDocument függvénye. A nyugta utólagos számlává alakítása is.
A Számla Agenttel kiállított nyugta jogilag **számítógéppel előállított nyugta**. Nem pótolja az online pénztárgépet, és nem e-nyugta. Mielőtt nyugtát állítasz ki, két kérdést kell eldönteni: adhatsz-e egyáltalán ilyen nyugtát, és elég-e az adott vevőnek a nyugta.
Ez az oldal a Számlázz.hu tudástára és a NAV tájékoztatói alapján foglalja össze a szabályokat.
A saját tevékenységed besorolását könyvelővel ellenőrizd.
## A nyugták fajtái [#a-nyugták-fajtái]
| Fajta | Mivel készül | A kassza tudja? |
| --- | --- | --- |
| Kézi nyugta | Nyomdai nyugtatömb, kézzel írva | Nem, de a napi NAV-összesítőhöz ad eszközt ([kézi nyugtatömb](/docs/nav-nyugta/kezi-nyugtatomb)) |
| Számítógéppel előállított nyugta | Számlázó program, például a Számlázz.hu felülete vagy a **Számla Agent** | **Igen**, ezt adja a `receipts.create()` |
| Gépi nyugta | Online pénztárgép (OPG) | Nem |
| E-nyugta | E-pénztárgép, például a Számlázz.hu ePénztárgép alkalmazása | Nem, ehhez nincs API |
Az Agent-nyugtát ne hívd e-nyugtának: e-nyugtát kizárólag e-pénztárgép adhat (8/2025. (III. 31.) NGM rendelet).
## Ki adhat Agent-nyugtát? [#ki-adhat-agent-nyugtát]
Csak az, akinek a tevékenysége **nem online pénztárgép-köteles**. A pénztárgép-kötelezettséget az NGM rendelet melléklete sorolja fel, a fontosabbak:
- gyógyszertár,
- kiskereskedelem (TEÁOR 47.1–47.7); csomagküldő és internetes kereskedelemnél (47.91) csak a nyílt árusítású üzlet és a bemutatóterem,
- vendéglátás (56.1 és 56.3), a **mozgó vendéglátás kivételével**,
- szálláshely-szolgáltatás (55.1–55.3),
- kölcsönzés (77.1–77.2, 77.33) és javítás (95.1–95.2).
Egy helyhez kötött bolt vagy büfé tehát online pénztárgéppel vagy e-pénztárgéppel ad nyugtát, a kasszával nem. Az online pénztárgép 2028. június 30-ig használható, utána e-pénztárgép kell.
A kassza nyugtája jellemzően ezekhez való: webshop és online jegy fizetés után, letölthető termék, food truck és más mozgó árusítás, valamint nem pénztárgép-köteles szolgáltatás (fodrászat, edzés, oktatás, parkolás, rendezvény).
A Számlázz.hu szerint nyugtát csak olyan fiókból lehet kiállítani, ahol be van állítva a NAV
Online adatkapcsolat. Ha az első nyugtánál fiókhibát kapsz, először ezt ellenőrizd a Számlázz.hu
felületén.
## Mikor elég a nyugta? [#mikor-elég-a-nyugta]
Nyugta akkor adható számla helyett, ha **mind a négy feltétel** teljesül:
1. a vevő nem adóalany és nem jogi személy,
2. az ellenérték nem éri el a **900 000 Ft**-ot,
3. a teljes összeget legkésőbb a teljesítéskor kifizetik,
4. a vevő nem kér számlát.
Ha bármelyik nem teljesül, számlát kell kiállítani. Devizás eladásnál a 900 000 Ft-os határt forintra átszámítva kell nézni.
## A döntés kódban: chooseDocument [#a-döntés-kódban-choosedocument]
A `chooseDocument()` a fenti szabályokat alkalmazza, és indoklással együtt adja vissza a döntést. Nem hív hálózatot.
```ts
import { chooseDocument } from 'kassza'
const dontes = chooseDocument({
grossTotal: 18_990,
currency: 'HUF',
buyer: { taxNumber: undefined },
paidByFulfillment: true,
invoiceRequested: false,
cashRegisterRequired: false,
})
console.log(dontes.type, dontes.reasons)
```
| Mező | Jelentés |
| --- | --- |
| `grossTotal`, `currency`, `exchangeRate` | A bruttó végösszeg. Devizánál az árfolyam is kell, mert a határ forintban van. |
| `buyer` | `taxNumber`, `euTaxNumber` vagy `isBusiness: true` esetén a vevő cég. |
| `paidByFulfillment` | `false`, ha nem fizetik ki legkésőbb a teljesítésig. |
| `invoiceRequested` | `true`, ha a vevő számlát kér. |
| `cashRegisterRequired` | `true`, ha a tevékenységed pénztárgép-köteles. |
A `type` értéke:
- `invoice`: legalább egy feltétel nem teljesül, a `reasons` megmondja, melyik,
- `cash-register`: a vevőnek nyugta járna, de a tevékenység pénztárgép-köteles, így Agent-nyugta nem adható (számlát viszont kiállíthatsz),
- `receipt`: mind a négy feltétel teljesül, nyugta adható.
A [fizetésből bizonylatot készítő](/docs/fizetesek) `issueForPayment()` ugyanezt a döntést használja.
## Ha a vevő utólag számlát kér [#ha-a-vevő-utólag-számlát-kér]
Ha a vevő a nyugta után számlát kér, a nyugtát sztornózni kell, és számlát kell kiállítani a vevő nevére. A `receipts.convertToInvoice()` ezt egy lépésben végzi el, és ismételt hívásnál sem állít ki második számlát:
```ts
const eredmeny = await kassza.receipts.convertToInvoice({
receiptNumber: 'NYGT-2026-118',
buyer: {
name: 'Példa Kft.',
zip: '1111',
city: 'Budapest',
address: 'Fő utca 1.',
taxNumber: '12345676-2-42',
},
})
console.log(eredmeny.reversal?.number, eredmeny.invoice.number, eredmeny.invoice.created)
```
A számla a nyugta tételeiből készül, a megjegyzésben hivatkozik a nyugtára és a sztornóra, a külső azonosítója `CONV/{nyugtaszám}`. A csak nyugtán használható áfakódokhoz (`ÁKK`, `MAA`, `EU`, `EUK`) a `vatMapping` opcióban kell megadni a számlán használandó kulcsot, a könyvelőddel egyeztetve.
## Kapcsolódó [#kapcsolódó]
2026. szeptember 1-jétől a számítógéppel előállított nyugtákról is napi összesítő kell.
Stripe, SimplePay, Barion, Revolut és PayPal fizetésből automatikusan nyugta vagy számla.
A nyugtakérés mezői, a kerekítés és a hívásazonosító.
# Telepítés
URL: https://www.kasszajs.hu/docs/alapok/telepites
> A kassza telepítése, az Agent kulcs beállítása és az első számla kiállítása.
A kassza egyetlen npm csomag, futásidejű függőség nélkül. Node.js 22 vagy újabb kell hozzá, de Bunon, Denón, Cloudflare Workersen és Vercel Edge-en is fut, mert csak a szabványos `fetch`, `FormData` és `Blob` API-kat használja.
## Követelmények [#követelmények]
- **Futtatókörnyezet:** Node.js 22+, Bun, Deno, Cloudflare Workers vagy Vercel Edge Runtime
- **Számlázz.hu fiók** Agent kulccsal (a fiók bármelyik csomagjával működik, amelyik engedi a Számla Agentet)
- **TypeScript** nem kötelező, de a csomag teljes típusdefiníciókkal érkezik
## Telepítés [#telepítés]
```package-install
kassza
```
## Az Agent kulcs beállítása [#az-agent-kulcs-beállítása]
Az Agent kulcsot a Számlázz.hu felületén hozod létre, a vezérlőpult alján. A kassza a `SZAMLAZZ_AGENT_KEY` környezeti változóból olvassa ki, így a kódban nem kell megadnod.
```dotenv title=".env"
SZAMLAZZ_AGENT_KEY=a-te-agent-kulcsod
```
Az Agent kulcs titok, és csak kisbetűs lehet. Soha ne kerüljön böngészőbe menő kódba vagy a
verziókezelőbe. A kassza a nagybetűs kulcsot a kérés elküldése előtt visszautasítja.
## Az első számla [#az-első-számla]
```ts twoslash title="szamla.ts"
import { createKassza } from 'kassza'
const kassza = createKassza()
const szamla = await kassza.invoices.create({
orderNumber: 'REND-1001',
buyer: {
name: 'Vevő Kft.',
zip: '1111',
city: 'Budapest',
address: 'Fő utca 1.',
email: 'vevo@example.hu',
},
items: [{ name: 'Webfejlesztés', quantity: 10, unit: 'óra', netUnitPrice: 15_000, vat: 27 }],
})
console.log(szamla.number, szamla.grossTotal)
// ^?
```
A kerekítést, a magyar idő szerinti dátumot, az XML felépítését, a session cookie-t és a hibakezelést a kassza végzi. Mivel a vevőnek van e-mail címe, a Számlázz.hu a számlát e-mailben is elküldi neki.
Fejlesztés közben használj Számlázz.hu teszt fiókot, a teszteseteidben pedig a
[mock klienst](/docs/kiegeszitok/teszteles), amely valódi hívás nélkül ugyanazt a validációt és
kerekítést futtatja.
## A kulcs ellenőrzése [#a-kulcs-ellenőrzése]
A `verifyCredentials()` egy ártalmatlan lekérdezéssel ellenőrzi a kulcsot. Hibás kulcsnál `false`, jó kulcsnál `true` az eredménye, fiókprobléma (például lejárt előfizetés) esetén hibát dob.
```ts
const ervenyes = await kassza.verifyCredentials()
```
## Következő lépések [#következő-lépések]
Agent kulcs vagy felhasználónév és jelszó, környezeti változók, több fiók.
Alapértelmezések, időkorlát, újrapróbálás, hookok.
# Hitelesítés (Agent kulcs)
URL: https://www.kasszajs.hu/docs/alapok/hitelesites
> Az Agent kulcs beállítása, a régi felhasználónév és jelszó, több fiók kezelése és a kulcs ellenőrzése.
A Számla Agent minden kérésnél hitelesítést vár. A javasolt mód az **Agent kulcs**: egy hosszú, véletlenszerű kulcs, amelyet a Számlázz.hu felületén hozhatsz létre és vonhatsz vissza. A kassza a kulcsot a kérés XML-jének `` elemébe teszi.
## Agent kulcs létrehozása [#agent-kulcs-létrehozása]
Az Agent kulcsot a Számlázz.hu fiókodban, a vezérlőpult alján tudod generálni. Egy fiókhoz több kulcs is tartozhat, így például a webshopodnak és a könyvelési integrációnak lehet külön kulcsa, amelyeket egymástól függetlenül vissza tudsz vonni.
## A kulcs átadása a kasszának [#a-kulcs-átadása-a-kasszának]
A legegyszerűbb, ha a `SZAMLAZZ_AGENT_KEY` környezeti változóba teszed. A `createKassza()` ezt olvassa, ha nem adsz meg `agentKey` opciót:
```dotenv title=".env"
SZAMLAZZ_AGENT_KEY=a-te-agent-kulcsod
```
```ts
import { createKassza } from 'kassza'
export const kassza = createKassza()
```
Ha a kulcs máshonnan jön, például egy titokkezelőből vagy a Cloudflare Workers `env` objektumából, add át közvetlenül:
```ts
const kassza = createKassza({ agentKey: env.SZAMLAZZ_AGENT_KEY })
```
Az Agent kulcs csak kisbetűs lehet. Ha nagybetűt tartalmaz (például rosszul másoltad ki), a
kassza már a kliens létrehozásakor `configuration` kategóriájú hibát dob, így a hibás kulcs
el sem jut a Számlázz.hu-hoz.
## Felhasználónév és jelszó [#felhasználónév-és-jelszó]
Régebbi integrációk felhasználónévvel és jelszóval hitelesítenek. A kassza ezt is támogatja, de új integrációhoz az Agent kulcsot ajánljuk, mert visszavonható, és nem a fiók jelszava.
```ts
const kassza = createKassza({
username: process.env.SZAMLAZZ_USERNAME,
password: process.env.SZAMLAZZ_PASSWORD,
})
```
Ha a felhasználó több fiókhoz is hozzáfér, a Számlázz.hu 164-es hibával utasítja el a kérést. Ilyenkor Agent kulcsot kell használni.
## Több fiók [#több-fiók]
Minden `createKassza()` hívás egy fiókhoz tartozó klienst ad vissza, saját session cookie-val. Több cég számlázásához hozz létre fiókonként egy klienst:
```ts
const kliensek = {
budapest: createKassza({ agentKey: process.env.KULCS_BUDAPEST }),
debrecen: createKassza({ agentKey: process.env.KULCS_DEBRECEN }),
}
```
## A kulcs ellenőrzése [#a-kulcs-ellenőrzése]
A `verifyCredentials()` egy ártalmatlan lekérdezéssel teszteli a kulcsot, bizonylatot nem hoz létre. Hibás kulcsnál `false`, jó kulcsnál `true` az eredménye. Fiókproblémánál, például lejárt előfizetésnél hibát dob, mert ezt embernek kell rendeznie.
```ts
if (!(await kassza.verifyCredentials())) {
throw new Error('Hibás Számlázz.hu Agent kulcs')
}
```
## Biztonság [#biztonság]
- A kulcsot csak szerveroldali kód lássa. Ne tedd `NEXT_PUBLIC_` vagy más, a böngészőbe kerülő környezeti változóba.
- A kassza hookjai (`onRequest`, `onResponse`, `onError`) soha nem kapják meg a kulcsot, így a naplóidba sem kerülhet be.
- Fejlesztéshez használj Számlázz.hu teszt fiókot, és a teszteket futtasd a [mock klienssel](/docs/kiegeszitok/teszteles).
- Ha egy kulcs kiszivárgott, vond vissza a Számlázz.hu felületén, és generálj újat.
# Kliens beállítása
URL: https://www.kasszajs.hu/docs/alapok/kliens-beallitasa
> A createKassza() összes opciója, az alapértelmezések, az időkorlát, az újrapróbálás és a hookok.
A kasszát a `createKassza()` függvénnyel hozod létre. Egy folyamatban (process) elég egyszer létrehozni és újrahasznosítani, így a session cookie is megmarad a hívások között.
```ts title="lib/kassza.ts"
import { createKassza } from 'kassza'
export const kassza = createKassza({
defaults: {
invoice: {
prefix: 'WEB',
paymentDueInDays: 8,
seller: { emailReplyTo: 'penzugy@example.hu', emailSubject: 'Elkészült a számlád' },
},
receipt: { prefix: 'NYGT', paymentMethod: 'bankkártya' },
},
})
```
## Opciók [#opciók]
| Opció | Típus | Alapérték | Leírás |
| --- | --- | --- | --- |
| `agentKey` | `string` | `SZAMLAZZ_AGENT_KEY` | Az Agent kulcs. Csak kisbetűs lehet. |
| `username`, `password` | `string` | – | Régi hitelesítés Agent kulcs helyett. |
| `defaults.invoice` | `InvoiceDefaults` | `{}` | Minden számlára érvényes alapértékek. |
| `defaults.receipt` | `ReceiptDefaults` | `{}` | Minden nyugtára érvényes alapértékek. |
| `cookieStore` | `CookieStore \| false` | memóriában | A session cookie tárolója. `false` kikapcsolja. |
| `timeoutMs` | `number` | `60000` | Egy kérés időkorlátja milliszekundumban. |
| `maxAttempts` | `number` | `3` | Próbálkozások száma biztonságos műveleteknél, legfeljebb `5`. |
| `retryDelayMs` | `number` | `1000` | Az első újrapróbálás előtti várakozás, utána duplázódik. |
| `fetch` | `typeof fetch` | `globalThis.fetch` | Egyedi fetch, például proxyhoz vagy teszthez. |
| `endpoint` | `string` | `https://www.szamlazz.hu/szamla/` | Az Agent címe. |
| `hooks` | `SzamlazzHooks` | – | Naplózási események, lásd lent. |
## Alapértelmezések számlára [#alapértelmezések-számlára]
A `defaults.invoice` mezői akkor érvényesülnek, ha a hívásban nem adod meg őket. A hívásban megadott érték mindig felülírja az alapértéket.
| Mező | Leírás |
| --- | --- |
| `prefix` | Számlaszám előtag, például `WEB` |
| `paymentDueInDays` | Fizetési határidő napokban a kelttől |
| `paymentMethod` | Fizetési mód, például `átutalás` |
| `currency`, `exchangeBank` | Pénznem és árfolyamot jegyző bank |
| `language` | A számla nyelve |
| `template` | Számlasablon |
| `eInvoice`, `downloadPdf` | E-számla és PDF letöltés |
| `sendEmail` | Küldjön-e értesítőt, ha a vevőnek van e-mail címe |
| `seller` | Bank, bankszámla, válaszcím, e-mail tárgy és szöveg, aláíró |
| `simpleItems`, `euVat`, `logoExtra`, `aggregator`, `guardian`, `articleIdentifierInvoice` | Speciális beállítások |
## Alapértelmezések nyugtára [#alapértelmezések-nyugtára]
| Mező | Leírás |
| --- | --- |
| `prefix` | Nyugta előtag, csak nagybetű és szám |
| `paymentMethod` | Fizetési mód, nyugtánál nincs beépített alapértéke |
| `currency`, `exchangeRate`, `exchangeBank` | Pénznem és árfolyam |
| `downloadPdf`, `template` | PDF letöltés és PDF sablon |
| `unit` | Alapértelmezett mennyiségi egység |
| `customerLedgerId` | Vevő főkönyvi azonosító |
## Időkorlát és megszakítás [#időkorlát-és-megszakítás]
Minden metódus utolsó, opcionális paramétere egy `{ signal }` objektum. A saját `AbortSignal`-od és a kliens `timeoutMs` beállítása közül az hat, amelyik előbb lejár.
```ts
const { pdf } = await kassza.invoices.getPdf('WEB-2026-12', {
signal: AbortSignal.timeout(5_000),
})
```
Időtúllépésnél `timeout` kategóriájú hibát kapsz. Ha te szakítod meg a kérést, a signal eredeti oka dobódik tovább.
## Hookok [#hookok]
A hookokkal naplózhatod és mérheted a Számla Agent hívásokat. A hookok soha nem kapják meg az Agent kulcsot és az XML tartalmát. Futtatható példát a [Hálózat és biztonság](/docs/alapok/halozat-es-biztonsag#újrapróbálás-és-naplózás) oldalon találsz.
```ts
const kassza = createKassza({
hooks: {
onRequest: ({ action, attempt }) => logger.debug({ action, attempt }),
onResponse: ({ action, status, durationMs }) => logger.info({ action, status, durationMs }),
onError: ({ action, error, willRetry }) =>
logger.warn({ action, code: error.code, category: error.category, willRetry }),
},
})
```
## Serverless és edge [#serverless-és-edge]
Serverless függvényekben minden hideg indítás új memóriát kap, ezért a memóriában tárolt session cookie elveszhet. Ilyenkor adj meg közös tárolót a `cookieStore` opcióval, lásd a [Munkamenet](/docs/alapok/munkamenet) oldalt.
# Hibakezelés, hibakódok
URL: https://www.kasszajs.hu/docs/alapok/hibakezeles
> A SzamlazzError felépítése, a hibakategóriák, a bizonytalan kimenetű hibák kezelése és a Számla Agent összes ismert hibakódja.
Ha egy Számla Agent kérés nem sikerül, az ok szinte mindig a kérés adataiban vagy a fiók beállításaiban van. Ugyanazt a kérést újra elküldeni ilyenkor nem segít, sőt, a Számlázz.hu ki is tilthatja a fiókot. Ezért a kassza minden hibát egyetlen típusba, a `SzamlazzError`-ba gyűjt, és megmondja, milyen kategóriába esik, és mit érdemes tenni.
## A SzamlazzError [#a-szamlazzerror]
Minden kassza metódus `SzamlazzError`-t dob. A `isSzamlazzError()` típusőrrel biztonságosan szűkítheted a `catch` ágban kapott értéket:
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.create(szamla)
} catch (error) {
if (!isSzamlazzError(error)) throw error
switch (error.category) {
case 'validation':
return { hiba: error.message, tipp: error.hint }
case 'auth':
case 'account':
riasztas(`Számlázz.hu fiókhiba: ${error.message}`)
throw error
default:
throw error
}
}
```
| Mező | Típus | Leírás |
| --- | --- | --- |
| `message` | `string` | Magyar hibaüzenet. A Számlázz.hu kódját `[57]` alakban az elejére teszi. |
| `code` | `number \| undefined` | A Számlázz.hu hibakódja. Kliensoldali hibánál nincs. |
| `category` | `SzamlazzErrorCategory` | A hiba kategóriája, lásd lent. |
| `retryable` | `boolean` | `true` karbantartásnál, hálózati hibánál és időtúllépésnél. |
| `hint` | `string \| undefined` | Magyar javítási tipp, ha ismert. |
| `action` | `AgentAction \| undefined` | A művelet, például `createInvoice`. |
| `httpStatus` | `number \| undefined` | A válasz HTTP státusza. |
| `rawResponse` | `string \| undefined` | A nyers válasz első 2000 karaktere. PDF-nél nincs. |
| `isDuplicate`, `isNotFound` | `boolean` | Rövidítés a `duplicate` és a `not_found` kategóriára. |
| `docsUrl` | `string \| undefined` | A hibakód saját oldala a [hibakódok](/docs/hibakodok) között, ha a kód ismert. |
Ne írj olyan kódot, amely egy sikertelen számlakészítést addig küld újra, amíg sikerül. Egy
hibás kérés újraküldve is hibás marad, a sok felesleges kérés pedig a fiók kitiltásához
vezethet. Egy kérést legfeljebb ötször szabad elküldeni, utána embernek kell ránéznie. Ha
ugyanazt a kérést több folyamat is küldheti, a kassza [`attemptLedger`](/docs/alapok/pontosan-egyszer#kitiltás-elleni-védelem-folyamatok-között)
opciója a folyamatok között is betartja ezt a korlátot.
## Amit a kassza újrapróbál [#amit-a-kassza-újrapróbál]
A kassza csak ott próbálkozik újra, ahol ez nem okozhat dupla bizonylatot: lekérdezéseknél, a befizetések felülírásánál, és a `callId`-val védett nyugtáknál. Csak a `retryable` hibáknál teszi ezt (`maintenance`, `network`, `timeout`), alapból háromszor, 1, majd 2 másodperc várakozással. A számlakészítést soha nem küldi újra.
A próbálkozások számát a `maxAttempts`, a várakozást a `retryDelayMs` opcióval állíthatod. A `maxAttempts` értéke 5 fölé nem mehet, mert a Számlázz.hu ennyit enged egy kérésre.
## Bizonytalan kimenet: számla készült vagy nem? [#bizonytalan-kimenet-számla-készült-vagy-nem]
Hálózati hiba vagy időtúllépés után nem tudhatod, hogy a Számlázz.hu megkapta-e a kérést. Az 56-os hibánál (`partial_success`) a számla biztosan elkészült, csak az értesítő e-mail nem ment ki. A 71-es és 152-es hibánál (`duplicate`) a rendelésszámmal már készült számla.
Mindegyik esetben ugyanaz a helyes lépés: **kérdezd le a számlát a rendelésszám alapján**, mielőtt újra kiállítanád. Ehhez minden számlánál adj meg `orderNumber`-t.
```ts
import { type CreateInvoiceInput, isSzamlazzError } from 'kassza'
const BIZONYTALAN = ['network', 'timeout', 'partial_success', 'duplicate']
async function szamlaz(orderNumber: string, adatok: CreateInvoiceInput) {
const meglevo = await kassza.invoices.find({ orderNumber })
if (meglevo) return meglevo.header.number
try {
const szamla = await kassza.invoices.create({ ...adatok, orderNumber })
return szamla.number
} catch (error) {
if (isSzamlazzError(error) && BIZONYTALAN.includes(error.category)) {
const letrejott = await kassza.invoices.find({ orderNumber })
if (letrejott) return letrejott.header.number
}
throw error
}
}
```
Ezt a lépéssort a kassza el is végzi helyetted: az `invoices.createOnce()` és a `receipts.createOnce()` kiállítás előtt megkeresi a bizonylatot, bizonytalan kimenet után pedig újra megkeresi, mielőtt hibát adna. A részletek a [Pontosan egyszer](/docs/alapok/pontosan-egyszer) oldalon vannak.
## Hol jelez hibát a Számlázz.hu? [#hol-jelez-hibát-a-számlázzhu]
A Számla Agent műveletenként és válaszverziónként máshogy jelez hibát. A kassza mindet ugyanúgy kezeli:
- **Fejlécben:** a `szlahu_error` és a `szlahu_error_code` fejléc URL-kódolt üzenetet és kódot tartalmaz.
- **Szöveges válaszban:** a törzs `[ERR]` jelöléssel kezdődik, utána jön az üzenet és egy Java stack trace. A kassza csak az üzenetet és a kódot tartja meg.
- **XML-ben:** a `false` mellett `` és `` elem van.
- **HTTP státusszal:** az 5xx válaszból `network`, minden más ismeretlen formátumból `unexpected_response` kategóriájú hiba lesz.
## Kliensoldali validáció [#kliensoldali-validáció]
Sok hibát a kassza már a kérés előtt észrevesz: hiányzó egységár, ismeretlen áfakulcs, kisbetűs nyugta előtag, túl sok vagy túl nagy melléklet. Ilyenkor `validation` kategóriájú hibát kapsz `code` nélkül, és kérés sem megy a Számlázz.hu-hoz.
## Tesztfiók [#tesztfiók]
Fejlesztéshez használj Számlázz.hu tesztfiókot. A tesztfiókban 10 percenként legfeljebb 500 számla készíthető, a túllépés 167-es (`rate_limit`) hibát ad. Automata tesztekhez ezért inkább a [mock klienst](/docs/kiegeszitok/teszteles) vagy a [hamis Számla Agentet](/docs/kiegeszitok/hamis-agent) használd, ezek nem hívnak hálózatot.
## Hibakategóriák [#hibakategóriák]
Két kategória nem a Számlázz.hu-tól jön, hanem a kassza védelmeiből: az `in_progress` azt jelenti, hogy egy másik folyamat éppen ugyanezt a bizonylatot állítja ki (a [`createOnce` zárja](/docs/alapok/pontosan-egyszer#egyidejű-hívások-egy-rendelés-egy-bizonylat) miatt), a `store_unavailable` pedig azt, hogy egy kötelezőnek beállított tároló (zár vagy `fail-closed` próbálkozás-napló) nem érhető el. Egyik esetben sem ment ki kérés, és egyik sem újrapróbálható automatikusan.
## Hibakódok [#hibakódok]
Az alábbi táblázat a kassza által ismert Számlázz.hu hibakódokat mutatja. Az ismeretlen kódú hiba `unknown` kategóriát kap, de a Számlázz.hu eredeti üzenete ilyenkor is megmarad a `message` mezőben.
Minden kódnak saját oldala is van a [hibakódok](/docs/hibakodok) között, magyarázattal és kassza-példával.
## Következő lépések [#következő-lépések]
Hogyan tartja életben a kassza a munkamenetet, és mit tegyél serverless környezetben.
createOnce, kitiltás elleni védelem folyamatok között, karbantartási szünet.
Hibák szimulálása mock klienssel, hálózat nélkül.
Elveszett válasz, karbantartás és részleges siker a valódi kassza kódon.
# Pontosan egyszer
URL: https://www.kasszajs.hu/docs/alapok/pontosan-egyszer
> Számla és nyugta pontosan egyszer a createOnce metódusokkal, egyidejű hívások és elosztott zár, védelem a fiók kitiltása ellen folyamatok között (attemptLedger), karbantartási szünet, az onDocument hook és a figyelmeztetések.
Egy webhook, egy várólista vagy egy türelmetlen felhasználó ugyanazt a kérést többször is elküldheti. A Számlázz.hu ugyanakkor nem engedi, hogy egy sikertelen kérést ötnél többször elküldj, és egy időtúllépés után nem tudhatod, elkészült-e a bizonylat. Ezt a három problémát oldják meg az alábbi eszközök.
## createOnce: lekérdezés, kiállítás, visszakeresés [#createonce-lekérdezés-kiállítás-visszakeresés]
Az `invoices.createOnce()` és a `receipts.createOnce()` ugyanazt csinálja, amit a [Hibakezelés](/docs/alapok/hibakezeles#bizonytalan-kimenet-számla-készült-vagy-nem) oldal kézzel leír:
1. megkeresi a bizonylatot, és ha már létezik, azt adja vissza,
2. ha nem létezik, kiállítja,
3. ha a kiállítás kimenete bizonytalan (hálózati hiba, időtúllépés, 56-os vagy dupla hiba), rövid várakozás után kétszer újra megkeresi.
```ts
const eredmeny = await kassza.invoices.createOnce({
orderNumber: 'WEB-1042',
paid: true,
buyer: { name: 'Vevő Kft.', zip: '1111', city: 'Budapest', address: 'Fő utca 1.' },
items: [{ name: 'Előfizetés', grossUnitPrice: 12_700, vat: 27 }],
})
console.log(eredmeny.number, eredmeny.created)
```
| Mező | Jelentés |
| --- | --- |
| `number` | A számla száma, akár most készült, akár korábban. |
| `created` | `true`, ha ez a hívás állította ki. |
| `externalId` | A külső azonosító, amivel a számlát keresi. |
| `invoice` | A kiállítás válasza, ha ez a hívás állította ki. |
| `details` | A lekérdezett számla, ha már létezett. |
A rendelésszám kötelező. A külső azonosító alapból a rendelésszám, a bizonylattípus jelével: díjbekérőnél `/D`, előlegszámlánál `/E`, végszámlánál `/V`, helyesbítőnél `/H`, szállítólevélnél `/SZL`. Így egy rendeléshez tartozó díjbekérő és számla nem keveredik össze. Saját `externalId` is megadható.
Nyugtánál a `receipts.createOnce()` a rendelésszámot hívásazonosítóként (`callId`) is használja, ha nem adsz meg mást. A visszatérési értéke `{ receipt, created }`.
| Opció | Alapérték | Jelentés |
| --- | --- | --- |
| `lookupFirst` | `true` | Kiállítás előtt megkeresi a bizonylatot. |
| `matchOrderNumber` | `true` | Számlánál a külső azonosító mellett a rendelésszámra is keres. |
| `recoveryDelayMs` | `1000` | A visszakeresések előtti várakozás (az első után duplázódik). |
| `signal` | – | Megszakítás. |
| `lock` | a `createOnceLock` | Hívásonkénti elosztott zár, vagy `false` a kikapcsoláshoz. |
| `lockTtlSeconds` | `600` | Meddig él a zár, ha a folyamat menet közben leáll. |
| `lockWaitMs` | `10000` | Meddig vár egy másik folyamat zárjára, mielőtt `in_progress` hibát ad. |
| `lockFailure` | `'throw'` | Mi legyen, ha a zár tárolója nem érhető el: `'throw'` vagy `'proceed'`. |
Ritkán előfordul, hogy a kiállítás kimenete bizonytalan, és a visszakeresés sem találja a
bizonylatot. Ilyenkor a kassza az eredeti hibát dobja tovább, `details.outcome: 'unknown'`
jelöléssel. Ne állítsd ki kézzel újra: később hívd meg ugyanígy a `createOnce()`-t, az előbb
megkeresi a bizonylatot.
## Egyidejű hívások: egy rendelés, egy bizonylat [#egyidejű-hívások-egy-rendelés-egy-bizonylat]
Ugyanaz a webhook kétszer is megérkezhet egyszerre, vagy két serverless példány dolgozhatja fel ugyanazt a rendelést. Ha két `createOnce()` hívás egyszerre keres, mindkettő azt látja, hogy még nincs számla, és mindkettő kiállítaná. Nyugtánál a Számlázz.hu a második kérést a hívásazonosító miatt elutasítja, számlánál viszont csak akkor, ha a fiókban be van kapcsolva a rendelésszám ismétlődésének tiltása.
A kassza ezért két szinten véd:
1. **Egy folyamaton belül** az azonos rendelésre egyszerre érkező hívások összevonódnak: az első kiállít, a többi ugyanazt a bizonylatot kapja `created: false` jelzéssel. Ez akkor is működik, ha kérésenként új kassza példányt hozol létre (serverless minta). Nem kell hozzá beállítás.
2. **Folyamatok között** egy közös tárolóban tartott, rövid életű zár véd. A zárat az kapja meg, aki elsőként kéri; a többi folyamat megvárja, majd visszakeresi a már elkészült bizonylatot.
```ts title="lib/kassza.ts"
import { Redis } from '@upstash/redis'
import { createKassza } from 'kassza'
import { upstashRedisStore } from 'kassza/stores'
const store = upstashRedisStore(Redis.fromEnv())
export const kassza = createKassza({
cookieStore: store,
attemptLedger: store,
createOnceLock: store,
})
```
A zárhoz olyan tároló kell, amely atomikusan tud „írni, ha még nincs” (`setIfAbsent`): a `kassza/stores` Redis, Upstash és Durable Object tárolója ilyen, a Cloudflare KV nem. Ha egy másik folyamat a várakozási időn belül nem végez, a kassza `in_progress` kategóriájú hibát dob: ilyenkor ne állítsd ki kézzel, hanem hívd meg később ugyanígy a `createOnce()`-t (webhookban adj vissza 5xx választ, és a szolgáltató újraküldi).
A `kassza/stores` `diagnoseStore(tároló)` függvénye kipróbálja a tárolót, és megmondja, mire alkalmas
(munkamenet, próbálkozás-napló, zár, bizonylatnapló).
## Kitiltás elleni védelem folyamatok között [#kitiltás-elleni-védelem-folyamatok-között]
A Számlázz.hu egy kérést legfeljebb ötször enged elküldeni. A kassza egy folyamaton belül ezt magától betartja, de ha ugyanazt a kérést több szerver, több serverless példány vagy egy újrapróbáló várólista küldi, a folyamatok nem tudnak egymásról. Erre való az `attemptLedger` opció: egy közös tároló, amelyben a kassza számolja az azonos kérések sikertelen próbálkozásait.
```ts title="lib/kassza.ts"
import { Redis } from '@upstash/redis'
import { createKassza } from 'kassza'
import { upstashRedisStore } from 'kassza/stores'
const store = upstashRedisStore(Redis.fromEnv())
export const kassza = createKassza({
cookieStore: store,
attemptLedger: store,
})
```
A tároló ugyanaz a felület, mint a munkamenet tárolója, így bármelyik [`kassza/stores`](/docs/alapok/munkamenet) adapter megfelel (a régi `kassza/cookie-stores` útvonal is működik). A kulcsot a kérés tartalmából képzi a kassza (SHA-256), az Agent kulcsot nem tárolja, és egy napig őrzi. Ha a tároló tud atomikus számlálót (`increment`, a Redis és Upstash adapterek igen), két egyszerre hibázó folyamat sem veszít el próbálkozást.
Ha a tároló nem érhető el, a kassza alapból továbbengedi a kérést, és a folyamaton belüli számlálóval véd (`attemptLedgerMode: 'fail-open'`), a hibát pedig az `onWarning` hooknak jelzi. Ha inkább ne menjen ki kérés, amíg a napló nem működik, állítsd `'fail-closed'`-ra: ilyenkor `store_unavailable` hibát kapsz. Ehhez a tárolót `resilient: false` beállítással hozd létre, különben az adapter maga elnyeli az olvasási hibát.
Öt sikertelen próbálkozás után a kassza már el sem küldi a kérést, hanem `attempt_limit` kategóriájú hibát dob. Ilyenkor embernek kell ránéznie. A hiba elhárítása után a számlálót a hibával törölheted:
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.create(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.category === 'attempt_limit') {
await riasztas(error.message)
await kassza.resetAttempts(error)
}
throw error
}
```
Sikeres válasz után a számláló magától törlődik. A próbálkozások közé csak a kérésből eredő hibák számítanak (például validáció, fiókhiba, hálózati hiba); a dupla bizonylat és a nem található bizonylat nem.
## Karbantartási szünet [#karbantartási-szünet]
Karbantartás (1-es hiba) idején a Számlázz.hu percekig nem érhető el. A `maintenanceCooldownMs` opcióval a kassza az első karbantartási hiba után a megadott ideig el sem küldi a kéréseket, hanem azonnal `maintenance` hibát ad. Így egy várólista nem égeti el a próbálkozásait, a pénztár pedig gyorsan átválthat tartalék megoldásra.
```ts
const kassza = createKassza({ maintenanceCooldownMs: 120_000 })
```
## onDocument: minden elkészült bizonylatról értesítés [#ondocument-minden-elkészült-bizonylatról-értesítés]
Az `onDocument` hook minden sikeres kiállítás, sztornó és befizetés után lefut, a bizonylat adataival. Jó hely a könyvelési napló, az e-mail sor vagy a statisztika frissítésére. Ha a hook hibát dob, a bizonylat attól még elkészült. Hogy ilyenkor mi történjen, az `onDocumentError` dönti el:
| `onDocumentError` | Viselkedés |
| --- | --- |
| `'warn'` (alap) | Figyelmeztet (`onWarning`, vagy ha nincs, a konzolra), és a hívás sikeresen visszatér. |
| `'throw'` | `DocumentHookError`-t dob, amely tartalmazza a már elkészült bizonylatot (`error.event`, `error.document`, `error.number`). |
| függvény | A te kezelőd kapja meg a hibát és az eseményt; ha az is dob, `DocumentHookError` lesz belőle. |
Ha a hook egy jogilag fontos naplót ír (például a [NAV napi összesítőhöz](/docs/nav-nyugta/napi-osszesito) használt [bizonylatnaplót](/docs/kiegeszitok/bizonylatnaplo)), használd a `'throw'` módot, hogy egy kimaradt bejegyzés ne maradjon észrevétlen. A hibából a bizonylat száma kiolvasható: ne állítsd ki újra, csak a naplóba írást pótold.
```ts
const kassza = createKassza({
hooks: {
onDocument: async (esemeny) => {
await naplo.mentes({
fajta: esemeny.kind,
muvelet: esemeny.action,
szam: esemeny.number,
})
},
},
})
```
| `kind` | `action` | Mikor |
| --- | --- | --- |
| `invoice` | `created` | Számla, díjbekérő, előleg-, vég- vagy helyesbítő számla elkészült. |
| `invoice` | `reversed` | Sztornó számla készült; a `reversedNumber` az eredeti szám. |
| `invoice` | `payment` | Befizetés rögzítése vagy törlése. |
| `receipt` | `created` | Nyugta készült. |
| `receipt` | `reversed` | Sztornónyugta készült; a `reversedNumber` az eredeti szám. |
## onWarning: a korábban csendes hibák [#onwarning-a-korábban-csendes-hibák]
Néhány hibát a kassza szándékosan elnyel, hogy a számlázás ne akadjon el: a munkamenet tárolójának kiesését, a próbálkozás-napló hibáját, a hookok kivételeit, a zár felszabadításának hibáját. Az `onWarning` hook ezeket mind megkapja, így naplózhatod vagy riaszthatsz rájuk:
```ts
const kassza = createKassza({
hooks: {
onWarning: (figyelmeztetes) => {
logger.warn({ fajta: figyelmeztetes.kind, muvelet: figyelmeztetes.operation }, figyelmeztetes.message)
},
},
})
```
A `kind` értéke `session`, `ledger`, `hook`, `document`, `lock`, `dedupe` vagy `cache`. A [megfigyelhetőség](/docs/kiegeszitok/megfigyelhetoseg) modul ezeket metrikaként is számolja.
## Kapcsolódó [#kapcsolódó]
A hibakategóriák és a bizonytalan kimenet kézi kezelése.
Webhookból automatikusan, pontosan egyszer.
Elveszett válasz és karbantartás szimulálása tesztben.
# Munkamenet (session cookie)
URL: https://www.kasszajs.hu/docs/alapok/munkamenet
> Hogyan menti és küldi vissza a kassza a Számlázz.hu munkameneti sütijét, és hogyan oszd meg serverless és edge környezetben.
A Számla Agent minden kérésnél hitelesít, ami időbe kerül. Ha viszont a kliens visszaküldi az előző válaszban kapott munkameneti sütit (`JSESSIONID`), a Számlázz.hu a meglévő munkamenetet használja, és gyorsabban válaszol. A süti tárolása a hívó fél dolga. A kasszában ez automatikus.
## Mit csinál a kassza? [#mit-csinál-a-kassza]
1. Az első kérésnél nincs tárolt süti, a Számlázz.hu új munkamenetet nyit, és a válasz `Set-Cookie` fejlécében visszaadja a sütit.
2. A kassza elmenti a sütit a `cookieStore`-ba.
3. A következő kéréseknél a kassza a `Cookie` fejlécben visszaküldi, és ha a Számlázz.hu frissíti, felülírja a tároltat.
4. Hitelesítési hibánál (`auth` kategória) a kassza törli a tárolt sütit, így a következő kérés tiszta lappal indul.
A Számlázz.hu 90 perc tétlenség után törli a munkamenetet. A kassza a sütit 85 percig tartja meg, így nem küld vissza lejárt munkamenetet.
A tárolókulcs az Agent kulcs hash-éből készül, például `szamlazz:session:3f1c…`. A kulcs maga
soha nem kerül a tárolóba, így ugyanaz a tároló több fiók munkamenetét is biztonságosan
tárolhatja.
## Hosszan futó szerveren [#hosszan-futó-szerveren]
Node.js szerveren, Bunon vagy Denón nincs teendőd. Az alapértelmezett tároló a memóriában van, és addig él, amíg a folyamat. Ehhez a klienst egyszer hozd létre, és mindenhol ugyanazt használd:
```ts title="lib/kassza.ts"
import { createKassza } from 'kassza'
export const kassza = createKassza()
```
Ha minden HTTP kérésnél új `createKassza()` hívás fut, minden kliensnek saját, üres
memóriatárolója lesz, és a munkamenet soha nem hasznosul újra.
## Serverless és edge [#serverless-és-edge]
Vercel Functions, AWS Lambda vagy Cloudflare Workers alatt a példányok jönnek-mennek, a memória nem közös. Ilyenkor adj meg közös tárolót a `kassza/cookie-stores` modulból:
```ts
import { Redis } from '@upstash/redis'
import { createKassza } from 'kassza'
import { upstashRedisCookieStore } from 'kassza/cookie-stores'
export const kassza = createKassza({
cookieStore: upstashRedisCookieStore(Redis.fromEnv()),
})
```
```ts
import { createKassza } from 'kassza'
import { cloudflareKvCookieStore } from 'kassza/cookie-stores'
export default {
async fetch(request: Request, env: Env) {
const kassza = createKassza({
agentKey: env.SZAMLAZZ_AGENT_KEY,
cookieStore: cloudflareKvCookieStore(env.KASSZA_KV),
})
return Response.json(await kassza.invoices.find({ orderNumber: 'REND-1001' }))
},
}
```
```ts
import Redis from 'ioredis'
import { createKassza } from 'kassza'
import { ioredisCookieStore } from 'kassza/cookie-stores'
export const kassza = createKassza({
cookieStore: ioredisCookieStore(new Redis(process.env.REDIS_URL)),
})
```
```ts
import { createClient } from 'redis'
import { createKassza } from 'kassza'
import { nodeRedisCookieStore } from 'kassza/cookie-stores'
const redis = await createClient({ url: process.env.REDIS_URL }).connect()
export const kassza = createKassza({ cookieStore: nodeRedisCookieStore(redis) })
```
A Cloudflare KV legalább 60 másodperces lejáratot fogad el, ezt az adapter magától betartja.
## Saját tároló [#saját-tároló]
Bármilyen kulcs-érték tárolót bekötheted, ha megírod a `get`, `set` és `delete` függvényt. A `set` harmadik paramétere a lejárat másodpercben.
```ts
import { customCookieStore } from 'kassza/cookie-stores'
const cookieStore = customCookieStore(
{
get: (key) => db.session.findValue(key),
set: (key, value, ttlSeconds) => db.session.upsert(key, value, ttlSeconds),
delete: (key) => db.session.remove(key),
},
{ prefix: 'webshop:', timeoutMs: 300 },
)
```
## Ha a tároló nem elérhető [#ha-a-tároló-nem-elérhető]
A munkamenet csak gyorsítás, ezért a tároló hibája nem akaszthatja meg a számlázást. Ha a Redis vagy a KV nem válaszol, a kassza munkamenet nélkül küldi el a kérést. Az adapterek alapból figyelmeztetést írnak a konzolra, ezt az `onError` opcióval a saját naplódba irányíthatod:
| Opció | Alapérték | Leírás |
| --- | --- | --- |
| `prefix` | – | Előtag minden tárolókulcs elé. |
| `timeoutMs` | nincs | Ennyi idő után a tároló műveletét sikertelennek veszi. |
| `onError` | `console.warn` | Hívódik, ha a tároló hibát dob vagy túllépi az időkorlátot. |
| `resilient` | `true` | `false` esetén a tároló hibája a hívóhoz is eljut. |
## Munkamenet törlése [#munkamenet-törlése]
Ha a Számlázz.hu felületén módosítod a fiók adatait, például a cégnevet vagy az e-mail címet, a változás csak új munkamenetben érvényesül. Ilyenkor hívd meg a `resetSession()` metódust:
```ts
await kassza.resetSession()
```
Ha egyáltalán nem szeretnél munkamenetet, add meg a `cookieStore: false` opciót. Ekkor minden kérés teljes hitelesítéssel fut, ami nagy forgalomnál érezhetően lassabb.
# Hálózat és biztonság
URL: https://www.kasszajs.hu/docs/alapok/halozat-es-biztonsag
> Tűzfalszabályok a Számlázz.hu felé és felől, időkorlát, újrapróbálás, naplózás hookokkal és az Agent kulcs védelme.
A kassza minden kérést HTTPS-en küld a `https://www.szamlazz.hu/szamla/` címre. Ezen az oldalon azt találod, amire egy szigorúbban védett szerveren szükséged lehet: IP-címek a tűzfalhoz, időkorlát, újrapróbálás és naplózás.
## Kimenő forgalom a Számlázz.hu felé [#kimenő-forgalom-a-számlázzhu-felé]
A Számlázz.hu a Cloudflare hálózatán keresztül érhető el. Ha a szervered csak engedélyezett címekre indíthat kapcsolatot, a 443-as porton engedd a Cloudflare alábbi IPv4 tartományait. A lista 2025. augusztus 1-je óta érvényes.
| IP-tartomány | IP-tartomány | IP-tartomány |
| --- | --- | --- |
| `173.245.48.0/20` | `103.21.244.0/22` | `103.22.200.0/22` |
| `103.31.4.0/22` | `141.101.64.0/18` | `108.162.192.0/18` |
| `190.93.240.0/20` | `188.114.96.0/20` | `197.234.240.0/22` |
| `198.41.128.0/17` | `162.158.0.0/15` | `104.16.0.0/13` |
| `104.24.0.0/14` | `172.64.0.0/13` | `131.0.72.0/22` |
Tartomány helyett egyszerűbb a `www.szamlazz.hu` domaint engedélyezni, ha a proxyd vagy a
tűzfalad támogatja a domain alapú szabályokat.
## Bejövő forgalom a Számlázz.hu felől [#bejövő-forgalom-a-számlázzhu-felől]
A Számlázz.hu a fizetési értesítéseket (IPN) és a többi, általa indított hívást az alábbi címekről küldi. Ha a webhook végpontodat IP alapján korlátozod, ezeket engedélyezd:
A lista a kódban is elérhető, és az `isSzamlazzIp()` ellenőrzi, hogy egy cím a Számlázz.hu-é-e:
```ts title="app/api/szamlazz/ipn/route.ts"
import { ipnOkResponse, isSzamlazzIp, readIpnNotification } from 'kassza/ipn'
export async function POST(request: Request) {
const ip = request.headers.get('x-forwarded-for')
if (!isSzamlazzIp(ip)) return new Response('Tiltott', { status: 403 })
const ertesites = await readIpnNotification(request)
await rendelesFizetve(ertesites)
return ipnOkResponse()
}
```
Az `x-forwarded-for` fejlécet a kliens is beállíthatja, ezért az `isSzamlazzIp()` a fejléc jobb
szélső címét vizsgálja, azt, amelyet a hozzád legközelebbi proxy fűzött hozzá. Ha egynél több
proxy áll előtted, add meg a `trustedProxies` opcióval, hányat ugorjon át. Cloudflare mögött a
`cf-connecting-ip` fejlécet is átadhatod. Platformonkénti táblázatot az
[IPN](/docs/befizetes-rogzitese/ipn) oldalon találsz.
## Időkorlát [#időkorlát]
Egy kérés alapból 60 másodpercig várhat válaszra, ezt a `timeoutMs` opcióval módosíthatod. Egy hívásra saját `AbortSignal`-t is adhatsz, például ha a webhook szolgáltatód 10 másodpercen belül választ vár:
```ts
const szamla = await kassza.invoices.create(adatok, { signal: AbortSignal.timeout(8_000) })
```
Ha a számlakészítés időtúllépéssel ér véget, a Számlázz.hu ettől még elkészíthette a számlát.
Előbb kérdezd le rendelésszám alapján, ahogy a [Hibakezelés](/docs/alapok/hibakezeles#bizonytalan-kimenet-számla-készült-vagy-nem)
oldal mutatja.
## Újrapróbálás és naplózás [#újrapróbálás-és-naplózás]
A biztonságosan ismételhető műveleteket a kassza hálózati hiba, 5xx válasz, időtúllépés és karbantartás esetén magától újrapróbálja, 1, majd 2 másodperc várakozással. A hookokkal minden próbálkozást naplózhatsz:
A hookok a műveletet, a próbálkozás sorszámát, a HTTP státuszt, az időtartamot és a hibát kapják meg. Az Agent kulcsot és a kérés XML-jét soha, így a naplóba nem kerülhet titok vagy vevőadat.
A `SzamlazzError.rawResponse` a Számlázz.hu válaszának elejét tartalmazza, amiben vevőnév vagy
cím is lehet. Ne küldd el változatlanul külső hibakövető szolgáltatásba, inkább a `code`, a
`category` és a `message` mezőt naplózd.
## Proxy és egyedi fetch [#proxy-és-egyedi-fetch]
A kassza a globális `fetch`-et használja, így vállalati proxy mögött elég a Node.js globális HTTP kliensét beállítani, például az `undici` csomaggal:
```ts title="instrumentation.ts"
import { ProxyAgent, setGlobalDispatcher } from 'undici'
const proxy = process.env.HTTPS_PROXY
if (proxy) setGlobalDispatcher(new ProxyAgent(proxy))
```
Ha csak a Számlázz.hu felé menő forgalmat akarod mérni vagy módosítani, add meg a saját `fetch` függvényedet:
```ts
export const kassza = createKassza({
fetch: async (url, init) => {
const valasz = await fetch(url, init)
metrika.novel('szamlazz_http_valasz', { status: valasz.status })
return valasz
},
})
```
## Az Agent kulcs védelme [#az-agent-kulcs-védelme]
- A kulcsot csak szerveroldali kód olvassa, soha ne kerüljön böngészőbe vagy mobilalkalmazásba.
- Titokkezelőben vagy környezeti változóban tárold, ne a forráskódban.
- Webshoponként és integrációnként használj külön kulcsot, így egy kiszivárgott kulcs a többit nem érinti.
- A kulcsot a Számlázz.hu felületén bármikor visszavonhatod. Visszavonás után a kassza `auth` kategóriájú hibát ad, a `verifyCredentials()` pedig `false`-t.
# Kérések küldése
URL: https://www.kasszajs.hu/docs/alapok/keresek-kuldese
> A Számla Agent egyetlen végpontja, a műveletek form mezői, a multipart kérés felépítése és amit a kassza ebből elvégez helyetted.
A Számla Agent minden művelete ugyanarra a címre érkezik: `https://www.szamlazz.hu/szamla/`. Hogy melyik műveletet kéred, azt nem az URL, hanem a `multipart/form-data` kérésben küldött XML fájl form mezőjének neve dönti el.
## Műveletek és form mezők [#műveletek-és-form-mezők]
A kasszában nem kell form mezőkkel foglalkoznod, minden műveletnek saját metódusa van. A táblázat azt is mutatja, melyik hívást próbálja újra a kassza hálózati hiba esetén.
## Mit küld el a kassza? [#mit-küld-el-a-kassza]
Egy számlakészítés a hálózaton nagyjából így néz ki. Az XML fájlként megy, nem szöveges mezőként. Ha szöveges mezőként küldenéd, a Számlázz.hu 53-as hibával válaszolna.
```http
POST /szamla/ HTTP/1.1
Host: www.szamlazz.hu
Accept: application/xml, application/pdf, text/plain, */*
Cookie: JSESSIONID=…
Content-Type: multipart/form-data; boundary=----kassza
------kassza
Content-Disposition: form-data; name="action-xmlagentxmlfile"; filename="request.xml"
Content-Type: text/xml; charset=UTF-8
……………
------kassza--
```
Mellékletnél a fájlok `attachfile1`, `attachfile2` és így tovább nevű mezőkben mennek, legfeljebb 5 darab, darabonként legfeljebb 2 MB.
A [sandboxban](/sandbox) minden futtatás után a Hálózat fülön látod a ténylegesen elküldött XML-t
és a szimulált választ.
## Egy XML, egy bizonylat [#egy-xml-egy-bizonylat]
Egy kérés pontosan egy számlát vagy nyugtát hoz létre. Több bizonylatot egymás után, külön hívásokkal állíts ki. Tömeges számlázásnál ne indíts egyszerre sok párhuzamos kérést, mert a Számlázz.hu a túlzott terhelést kitiltással büntetheti.
```ts
for (const rendeles of kiszamlazando) {
await szamlaz(rendeles)
}
```
## Elgépelt mezőnevek [#elgépelt-mezőnevek]
Az Agent XML elemnevei kis- és nagybetűérzékenyek, és a sorrendjük is kötött. Egy elgépelt elemnév 57-es hibát okoz, vagy ami rosszabb, a számla elkészül, de a kért beállítás csendben elmarad. A kasszában ilyen hiba nem fordulhat elő: az XML-t típusos objektumból, az XSD szerinti sorrendben építi fel, az elgépelt mezőnevet pedig már a TypeScript jelzi.
```ts twoslash
// @errors: 2561
import { createKassza } from 'kassza'
const kassza = createKassza({ agentKey: 'kulcs' })
await kassza.invoices.create({
buyer: { name: 'Vevő Kft.', zip: '1111', city: 'Budapest', address: 'Fő utca 1.' },
items: [{ name: 'Tanácsadás', netUnitPrice: 20_000, vat: 27 }],
simpleitems: true,
})
```
## Válaszformátum [#válaszformátum]
A Számla Agent a kérés `valaszVerzio` értékétől függően szöveggel, PDF-fel vagy XML-lel válaszol. A kassza mindig az XML választ kéri, mert abban a számlaszám, az összegek és, ha kérted, a Base64 kódolású PDF is benne van. Te ebből típusos objektumot kapsz, a PDF pedig `Uint8Array` lesz.
## Tesztelés [#tesztelés]
Tesztfiókban 10 percenként legfeljebb 500 számlát készíthetsz. Fejlesztés közben ez bőven elég, automata tesztekhez viszont a [`kassza/testing`](/docs/kiegeszitok/teszteles) mock kliensét használd.
## Több webshop egy fiókban [#több-webshop-egy-fiókban]
Ha ugyanabból a Számlázz.hu fiókból több webshop számláz, adj mindegyiknek saját számlaszám előtagot. Az előtagot előbb a Számlázz.hu felületén, a Beállítások / Előtagok menüpontban kell rögzíteni, különben 202-es hibát kapsz.
```ts
export const webshopKassza = createKassza({ defaults: { invoice: { prefix: 'WEB' } } })
export const piaciKassza = createKassza({ defaults: { invoice: { prefix: 'PIAC' } } })
```
## Alacsony szintű konstansok [#alacsony-szintű-konstansok]
Saját eszközökhöz a kassza a nyers értékeket is exportálja a `kassza` csomagból:
| Konstans | Érték |
| --- | --- |
| `SZAMLAZZ_AGENT_URL` | `'https://www.szamlazz.hu/szamla/'` |
| `AGENT_ACTIONS` | A művelet és a form mező párjai, például `createInvoice: 'action-xmlagentxmlfile'` |
| `MAX_INVOICE_ATTACHMENTS` | `5` |
| `AGENT_ERROR_CODES` | Az ismert hibakódok üzenettel, kategóriával és tippel |
## Következő lépések [#következő-lépések]
Az első számla minden mezővel és szabállyal.
Gyorsabb kérések a munkamenet újrahasznosításával.
# Áttérés a szamlazz.js-ről
URL: https://www.kasszajs.hu/docs/alapok/migracio-szamlazz-js
> Lépésről lépésre áttérés a szamlazz.js csomagról a kasszára - megfeleltetési táblázat, viselkedésbeli különbségek és őszinte összehasonlítás.
A [szamlazz.js](https://www.npmjs.com/package/szamlazz.js) a magyar Node.js közösség régóta használt Számlázz.hu kliense. Ha működik nálad, nem kötelező váltani. Ez az oldal annak szól, aki szeretné kihasználni a kassza többletét (pontosan egyszer kiállítás, nyugták, fizetési szolgáltatók, NAV-összesítő, tesztelés), és tudni akarja, mennyi munka az áttérés.
Az összevetés a szamlazz.js 9.2.0 nyilvános README-je alapján készült. Ha valamelyik állítás elavult, jelezd egy [hibajegyben](https://github.com/futozs/kassza/issues).
## Megfeleltetés [#megfeleltetés]
| szamlazz.js | kassza |
| --- | --- |
| `new Client({ authToken })` | `createKassza({ agentKey })`, vagy a `SZAMLAZZ_AGENT_KEY` környezeti változó |
| `new Client({ user, password })` | `createKassza({ username, password })` (a kulcs ajánlott) |
| `requestInvoiceDownload: true` | `downloadPdf` (alapból `true`) számlánként vagy a `defaults.invoice`-ban |
| `eInvoice: true` | `eInvoice: true` számlánként vagy a `defaults.invoice`-ban |
| `timeout: 1000` | `timeoutMs: 1000` (alapból 60 000) |
| `new Seller({ bank: { name, accountNumber }, email: { replyToAddress, subject, message } })` | `seller: { bank, bankAccount, emailReplyTo, emailSubject, emailText }` |
| `new Buyer({ name, zip, city, address, taxNumber, postAddress })` | `buyer: { name, zip, city, address, taxNumber, postal }` |
| `new Item({ label, quantity, unit, vat, netUnitPrice })` | `{ name, quantity, unit, vat, netUnitPrice }` |
| `new Invoice({ ... })` + `client.issueInvoice(invoice)` | `kassza.invoices.create({ ... })`, vagy inkább `invoices.createOnce({ orderNumber, ... })` |
| `prepaymentInvoice: true` | `type: 'advance'` |
| `adjustmentInvoiceNumber: 'X'` | `type: 'corrective', correctedInvoiceNumber: 'X'` |
| `PaymentMethods.BankTransfer`, `Currencies.Ft`, `Languages.Hungarian` | sima szövegek: `paymentMethod: 'átutalás'`, `currency: 'HUF'`, `language: 'hu'` |
| `client.getInvoiceData({ invoiceId, pdf })` | `kassza.invoices.get(invoiceId, { includePdf })`, vagy `find()`, ha a hiányzó számla nem hiba |
| `client.getInvoiceData({ orderNumber })` | `kassza.invoices.get({ orderNumber })` |
| `client.reverseInvoice({ invoiceId, eInvoice, requestInvoiceDownload })` | `kassza.invoices.reverse({ invoiceNumber, eInvoice, downloadPdf })` |
| válasz: `invoiceId`, `netTotal` (szöveg), `grossTotal` (szöveg), `customerAccountUrl`, `pdf` (Buffer) | válasz: `number`, `netTotal` (szám), `grossTotal` (szám), `buyerAccountUrl`, `pdf` (`Uint8Array`) |
## Előtte és utána [#előtte-és-utána]
```ts title="szamlazz.js"
const client = new Client({ authToken: process.env.SZAMLAZZ_AGENT_KEY, requestInvoiceDownload: true })
const invoice = new Invoice({
paymentMethod: PaymentMethods.BankTransfer,
currency: Currencies.Ft,
language: Languages.Hungarian,
seller: new Seller({}),
buyer: new Buyer({ name: 'Vevő Kft.', zip: '1111', city: 'Budapest', address: 'Fő utca 1.' }),
items: [new Item({ label: 'Előfizetés', quantity: 1, unit: 'db', vat: 27, grossUnitPrice: 12700 })],
})
const { invoiceId, pdf } = await client.issueInvoice(invoice)
```
```ts title="kassza"
import { createKassza } from 'kassza'
const kassza = createKassza()
const { number, created } = await kassza.invoices.createOnce({
orderNumber: 'WEB-1042',
paymentMethod: 'átutalás',
buyer: { name: 'Vevő Kft.', zip: '1111', city: 'Budapest', address: 'Fő utca 1.' },
items: [{ name: 'Előfizetés', quantity: 1, unit: 'db', vat: 27, grossUnitPrice: 12_700 }],
})
```
## Amire figyelj [#amire-figyelj]
1. **Adj meg rendelésszámot, és használd a `createOnce()`-t.** A kassza ezzel keresi meg a számlát, mielőtt kiállítaná, és bizonytalan hiba (időtúllépés, hálózat) után is. Így egy újraküldött webhook nem állít ki második számlát. Lásd [Pontosan egyszer](/docs/alapok/pontosan-egyszer).
2. **A kassza a számlát soha nem küldi újra magától.** Lekérdezéseket igen, legfeljebb ötször, mert a Számlázz.hu kitiltja, aki ciklusban próbálkozik. Ha eddig saját újrapróbáló ciklusod volt a kiállítás körül, töröld.
3. **Az összegeket a kassza számolja.** Add meg a `netUnitPrice`-t (cégnek) vagy a `grossUnitPrice`-t (magánszemélynek) és az áfakulcsot, a kassza a Számlázz.hu kerekítési szabályai szerint számol. Kézzel számolt nettó, áfa és bruttó ne menjen ki.
4. **A dátumok Budapest szerint számítanak.** Ha nem adsz meg dátumot, a kassza a budapesti mai napot használja, így éjfél után sem kerül a számla előző napra.
5. **A hibák típusosak.** A `SzamlazzError` `category` mezője megmondja, mi a teendő (`validation`, `duplicate`, `partial_success`, `network` …), és a `docsUrl` a hibakód [saját oldalára](/docs/hibakodok) mutat.
6. **Az összegek számok, a PDF `Uint8Array`.** Node.js alatt fájlba írás előtt nem kell átalakítani: a `fs.writeFile` elfogadja.
## Őszinte összehasonlítás [#őszinte-összehasonlítás]
| Szempont | szamlazz.js | kassza |
| --- | --- | --- |
| Érettség, ismertség | Évek óta használt, ismert, nagy közösség | Fiatal, kevés nyilvános felhasználó |
| Futásidejű függőségek | 6 (például `axios`, `xml2js`, `tough-cookie`) | 0 |
| Futtatókörnyezet | Node.js | Node.js, Bun, Deno, Cloudflare Workers, edge (a CI mind a négyet futtatja) |
| Számla, sztornó, lekérdezés | Igen | Igen |
| Nyugta, díjbekérő, befizetés, adószám-lekérdezés | A README nem írja | Igen |
| Pontosan egyszer kiállítás, több folyamat között is | A README nem írja | `createOnce`, `createOnceLock` |
| Fizetési szolgáltatók (Stripe, SimplePay, Barion, Revolut, PayPal) | Nincs | Webhook-kezelők és `issueForPayment` |
| NAV nyugta napi összesítő | Nincs | `kassza/reports`, `kassza/nav`, `kassza/journal` |
| Tesztelés hálózat nélkül | A README nem írja | Mock kliens és hamis Számla Agent |
| Magyar hibaüzenetek, hibakód-tábla | A README nem írja | Igen, kódonként saját oldallal |
Ha csak számlát állítasz ki egy Node.js szerverről, és a szamlazz.js megbízhatóan működik nálad, a váltás haszna kisebb. Ha webhookból, serverless környezetből vagy nyugtával dolgozol, a kassza pont ezeket a helyzeteket kezeli.
# Számla létrehozás
URL: https://www.kasszajs.hu/docs/szamla-letrehozas
> Számla, díjbekérő, előleg-, vég- és helyesbítő számla, szállítólevél kiállítása a kassza invoices.create() metódusával.
Számlát a `kassza.invoices.create()` metódussal állítasz ki. Ugyanez a metódus készít díjbekérőt, előleg-, vég- és helyesbítő számlát, valamint szállítólevelet is, a típust a `type` mező adja meg. A kassza kiszámolja a tételek összegeit, összeállítja a Számla Agent XML-jét, elküldi, és a választ típusos objektumként adja vissza.
```ts
import { createKassza } from 'kassza'
const kassza = createKassza()
const szamla = await kassza.invoices.create({
orderNumber: 'REND-1001',
buyer: { name: 'Vevő Kft.', zip: '1111', city: 'Budapest', address: 'Fő utca 1.' },
items: [{ name: 'Tanácsadás', quantity: 2, unit: 'óra', netUnitPrice: 20_000, vat: 27 }],
})
console.log(szamla.number, szamla.grossTotal)
```
Minden mező, ami a számlára kerülhet: vevő, tételek, dátumok, pénznem, mellékletek.
A CreatedInvoice mezői, a PDF, és a számlakészítésnél előforduló hibák.
Futtatható példák a ténylegesen elküldött XML-lel és a Számlázz.hu válaszával.
Bizonylattípusok, áfakulcsok, kerekítés, devizák, rendelésszám, e-mail értesítő.
A számlakészítés nem idempotens: ha a kérés kétszer ér be, két számla készül. A kassza ezért
soha nem próbálja újra magától. Bizonytalan hiba után előbb kérdezd le a számlát a rendelésszám
alapján, lásd a [Hibakezelés](/docs/alapok/hibakezeles#bizonytalan-kimenet-számla-készült-vagy-nem)
oldalt. Az `invoices.createOnce()` ezt el is végzi helyetted: lásd [Pontosan
egyszer](/docs/alapok/pontosan-egyszer).
# Kérés
URL: https://www.kasszajs.hu/docs/szamla-letrehozas/keres
> Az invoices.create() bemenete mezőről mezőre, a Számla Agent XML elemeivel, alapértékekkel és a kassza által ellenőrzött szabályokkal.
```ts
kassza.invoices.create(input: CreateInvoiceInput, options?: { signal?: AbortSignal }): Promise
```
A kassza a bemenetből `xmlszamla` XML-t épít, és `multipart/form-data` POST kérésben, az `action-xmlagentxmlfile` form mezőben küldi el a `https://www.szamlazz.hu/szamla/` címre. A mellékletek az `attachfile1` … `attachfile5` mezőkbe kerülnek.
Kötelező csak a **vevő** (`buyer`) és legalább egy **tétel** (`items`). Minden más mezőnek van alapértéke, vagy elhagyható.
Az alábbi táblázatokban az **XML elem** oszlop a Számla Agent mezőjét mutatja, így a hivatalos
dokumentáció bármelyik mezőjét megtalálod a kasszában. A kassza az elemeket az XSD által előírt
sorrendben írja ki.
## Fejléc [#fejléc]
| Mező | XML elem | Alapérték | Leírás |
| --- | --- | --- | --- |
| `type` | `elolegszamla`, `vegszamla`, `helyesbitoszamla`, `dijbekero`, `szallitolevel` | `'invoice'` | A bizonylat típusa, lásd [Bizonylattípusok](/docs/szamla-letrehozas/beallitasok-es-szabalyok/bizonylattipusok). |
| `advanceInvoiceNumber` | `elolegSzamlaszam` | – | Végszámlánál az előlegszámla száma. |
| `correctedInvoiceNumber` | `helyesbitettSzamlaszam` | – | Helyesbítő számlánál kötelező. |
| `issueDate` | `keltDatum` | ma | A számla kelte. `Date` vagy `'YYYY-MM-DD'`. |
| `fulfillmentDate` | `teljesitesDatum` | a kelte | A teljesítés dátuma. |
| `dueDate` | `fizetesiHataridoDatum` | kelte + `paymentDueInDays` | A fizetési határidő. |
| `paymentDueInDays` | – | `0` | Határidő napokban a keltétől. A `dueDate`-tel együtt nem adható meg. |
| `paymentMethod` | `fizmod` | `'Átutalás'` | Fizetési mód, szabad szöveg, például `'bankkártya'`. |
| `paid` | `fizetve` | – | `true` esetén a számla kifizetettként jelenik meg. |
| `currency` | `penznem` | `'HUF'` | Pénznem, lásd [Támogatott devizanemek](/docs/szamla-letrehozas/beallitasok-es-szabalyok/penznemek). |
| `exchangeRate` | `arfolyam` | – | Devizás számla árfolyama. MNB árfolyamnál elhagyható. |
| `exchangeBank` | `arfolyamBank` | `'MNB'` devizánál | Az árfolyamot jegyző bank. |
| `language` | `szamlaNyelve` | `'hu'` | A számla nyelve, lásd [Számlasablon és nyelvek](/docs/szamla-letrehozas/beallitasok-es-szabalyok/szamlasablon-es-nyelvek). |
| `comment` | `megjegyzes` | – | A számla megjegyzése. |
| `orderNumber` | `rendelesSzam` | – | A saját rendelésazonosítód. Mindig add meg, lásd [Rendelésszám](/docs/szamla-letrehozas/beallitasok-es-szabalyok/rendelesszam). |
| `proformaNumber` | `dijbekeroSzamlaszam` | – | A díjbekérő száma, amelyből a számla készül. |
| `externalId` | `szamlaKulsoAzon` | – | Külső azonosító, amellyel később lekérdezheted a számlát. |
| `prefix` | `szamlaszamElotag` | – | Számlaszám előtag. Csak a fiókban rögzített előtag használható (202-es hiba). |
| `template` | `szamlaSablon` | fiók alapértelmezés | Számlakép, például `'SzlaMost'`. |
| `eInvoice` | `eszamla` | `false` | `true` esetén e-számla készül. |
| `downloadPdf` | `szamlaLetoltes` | `true` | Kérje-e a PDF-et a válaszban. |
## Vevő (buyer) [#vevő-buyer]
| Mező | XML elem | Leírás |
| --- | --- | --- |
| `name` | `nev` | **Kötelező.** A vevő neve. |
| `zip` | `irsz` | **Kötelező.** Irányítószám. |
| `city` | `telepules` | **Kötelező.** Település. |
| `address` | `cim` | **Kötelező.** Utca, házszám. |
| `country` | `orszag` | Ország, külföldi vevőnél. |
| `email` | `email` | E-mail cím. Ha megadod, a Számlázz.hu elküldi a számlát. Több címet vesszővel választhatsz el. |
| `sendEmail` | `sendEmail` | `false` esetén nem megy értesítő. Alapból `true`, ha van e-mail cím. |
| `taxpayerType` | `adoalany` | `'hungarianTaxNumber'`, `'euBusiness'`, `'nonEuBusiness'`, `'noTaxNumber'` vagy `'unknown'`. |
| `taxNumber` | `adoszam` | Magyar adószám, például `'12345676-1-42'`. |
| `groupTaxNumber` | `csoportazonosito` | Csoportos adószám (csoportazonosító). |
| `euTaxNumber` | `adoszamEU` | Közösségi adószám, például `'DE123456789'`. |
| `postal` | `postazasiNev`, `postazasiOrszag`, `postazasiIrsz`, `postazasiTelepules`, `postazasiCim` | Postázási cím, ha eltér a számlázási címtől. |
| `ledger` | `vevoFokonyv` | Könyvelési adatok, lásd lent. |
| `identifier` | `azonosito` | Partner azonosító a fiók partnertörzsében. |
| `signatoryName` | `alairoNeve` | A vevő aláírójának neve. |
| `phone` | `telefonszam` | Telefonszám. |
| `comment` | `megjegyzes` | Megjegyzés a vevőhöz. |
Az `identifier` alapján a Számlázz.hu összekapcsolja a vevőt a partnertörzs bejegyzésével, és
frissíti annak adatait. Ha egy azonosító két vevőhöz tartozik, a vevői fiók linkjével az egyik
vevő a másik bizonylatait is láthatja. Egy azonosító mindig csak egy vevőé legyen.
## Tételek (items) [#tételek-items]
Minden tételnél a `name`, a `vat` és pontosan egy ár kell: `netUnitPrice` vagy `grossUnitPrice`. A nettó, áfa és bruttó értéket a kassza számolja ki a [kerekítési szabályok](/docs/szamla-letrehozas/beallitasok-es-szabalyok/kerekites) szerint.
| Mező | XML elem | Alapérték | Leírás |
| --- | --- | --- | --- |
| `name` | `megnevezes` | – | **Kötelező.** A tétel megnevezése. |
| `vat` | `afakulcs` | – | **Kötelező.** Áfakulcs, például `27` vagy `'AAM'`, lásd [Áfakulcsok](/docs/szamla-letrehozas/beallitasok-es-szabalyok/afakulcsok). |
| `netUnitPrice` | `nettoEgysegar` | – | Nettó egységár, nettó alapú (B2B) számításhoz. |
| `grossUnitPrice` | – | – | Bruttó egységár, bruttó alapú (B2C) számításhoz. A kassza ebből számolja a nettó egységárat. |
| `quantity` | `mennyiseg` | `1` | Mennyiség. Nem lehet 0, végszámlán és helyesbítőn negatív is lehet. |
| `unit` | `mennyisegiEgyseg` | `'db'` | Mennyiségi egység. |
| `netAmount`, `vatAmount`, `grossAmount` | `nettoErtek`, `afaErtek`, `bruttoErtek` | számolt | Csak akkor add meg, ha pontosan tudod, mit csinálsz. Mindhármat egyszerre kell megadni. |
| `identifier` | `azonosito` | – | Cikkszám vagy termékazonosító. |
| `comment` | `megjegyzes` | – | A tétel megjegyzése. |
| `ledger` | `tetelFokonyv` | – | Könyvelési adatok, lásd lent. |
| `dataDeletionCode` | `torloKod` | – | Adattörlő kód, lásd [Adattörlő kód](/docs/szamla-letrehozas/beallitasok-es-szabalyok/adattorlo-kod). |
| `marginVatBase` | `arresAfaAlap` | – | Különbözeti adózásnál az árrés áfa alapja. |
```ts
items: [
{ name: 'Webfejlesztés', quantity: 10, unit: 'óra', netUnitPrice: 15_000, vat: 27 },
{ name: 'Szakácskönyv', grossUnitPrice: 3_500, vat: 5 },
{ name: 'Kedvezmény', netUnitPrice: -5_000, vat: 27 },
]
```
## Eladó (seller) [#eladó-seller]
Az eladó cégadatai a Számlázz.hu fiókból jönnek, a kérésben csak az alábbiakat írhatod felül. Ha minden számlán ugyanaz, tedd a `defaults.invoice.seller`-be.
| Mező | XML elem | Leírás |
| --- | --- | --- |
| `bank` | `bank` | A bank neve. |
| `bankAccount` | `bankszamlaszam` | Bankszámlaszám. |
| `emailReplyTo` | `emailReplyto` | Az értesítő e-mail válaszcíme. |
| `emailSubject` | `emailTargy` | Az értesítő e-mail tárgya. |
| `emailText` | `emailSzoveg` | Az értesítő e-mail szövege, lásd [Számlaértesítő](/docs/szamla-letrehozas/beallitasok-es-szabalyok/ertesito-email). |
| `signatoryName` | `alairoNeve` | Az eladó aláírójának neve. |
## Mellékletek (attachments) [#mellékletek-attachments]
Az értesítő e-mailhez legfeljebb 5 fájlt csatolhatsz, fájlonként legfeljebb 2 MB-ot. A mellékletek csak e-mailben mennek ki, ezért a vevőnek kell e-mail cím, és a `sendEmail` nem lehet `false`.
```ts
attachments: [
{ filename: 'aszf.pdf', content: aszfPdf, contentType: 'application/pdf' },
{ filename: 'garancia.txt', content: 'A termékre 2 év garancia vonatkozik.' },
]
```
A `content` lehet `Uint8Array`, `ArrayBuffer`, `Blob` vagy szöveg.
## Fuvarlevél (waybill) [#fuvarlevél-waybill]
Szállítólevélnél vagy fuvarleveles számlaképnél (`template: 'SzlaFuvarlevelesAlap'`) a `waybill` mező a `fuvarlevel` blokkot tölti ki.
| Mező | XML elem | Leírás |
| --- | --- | --- |
| `courier` | `futarSzolgalat` | `'TOF'`, `'PPP'`, `'SPRINTER'`, `'FOXPOST'`, `'MPL'`, `'GLS'` vagy `'EMPTY'`. |
| `barcode` | `vonalkod` | Általános vonalkód, ha a futárszolgálat saját adatai hiányoznak. |
| `comment` | `megjegyzes` | Megjegyzés a fuvarlevélen. |
| `transOFlex` | `tof` | Trans-o-flex adatok: `id`, `shipmentId`, `packageCount`, `countryCode`, `zip`, `service`. |
| `pickPackPoint` | `ppp` | Pick Pack Pont: `barcodePrefix`, `barcodePostfix`. |
| `sprinter` | `sprinter` | Sprinter: `id`, `senderCode`, `directionCode`, `packageCount`, `barcodePostfix`, `deliveryTime`. |
| `mpl` | `mpl` | MPL: `customerCode`, `barcode`, `weight` kötelező, `services`, `declaredValue` opcionális. |
## Könyvelési adatok (ledger) [#könyvelési-adatok-ledger]
A könyvelő programba exportált számlákhoz a vevőnél és a tételeknél főkönyvi adatokat is megadhatsz.
| Mező | XML elem | Leírás |
| --- | --- | --- |
| `buyer.ledger.bookingDate` | `konyvelesDatum` | Könyvelés dátuma. |
| `buyer.ledger.buyerId` | `vevoAzonosito` | Vevő azonosító a könyvelésben. |
| `buyer.ledger.ledgerAccount` | `vevoFokonyviSzam` | Vevő főkönyvi száma. |
| `buyer.ledger.continuousFulfillment` | `folyamatosTelj` | Folyamatos teljesítés. |
| `buyer.ledger.settlementPeriodStart`, `settlementPeriodEnd` | `elszDatumTol`, `elszDatumIg` | Elszámolási időszak. |
| `items[].ledger.economicEvent` | `gazdasagiEsem` | Gazdasági esemény. |
| `items[].ledger.vatEconomicEvent` | `gazdasagiEsemAfa` | Áfa gazdasági esemény. |
| `items[].ledger.revenueLedgerAccount` | `arbevetelFokonyviSzam` | Árbevétel főkönyvi szám. |
| `items[].ledger.vatLedgerAccount` | `afaFokonyviSzam` | Áfa főkönyvi szám. |
| `items[].ledger.settlementPeriodStart`, `settlementPeriodEnd` | `elszDatumTol`, `elszDatumIg` | A tétel elszámolási időszaka. |
## Speciális mezők [#speciális-mezők]
Ezekre a legtöbb integrációnak nincs szüksége. Csak akkor használd őket, ha a könyvelőd vagy a Számlázz.hu ügyfélszolgálata kifejezetten kéri.
| Mező | XML elem | Leírás |
| --- | --- | --- |
| `simpleItems` | `simpleItems` | Egyszerűsített számlakép utazásszervezőknek, lásd [Utazásszervezők](/docs/szamla-letrehozas/beallitasok-es-szabalyok/utazasszervezok). |
| `euVat` | `eusAfa` | A számla nem tartalmaz magyar áfát, lásd [Áfakulcsok](/docs/szamla-letrehozas/beallitasok-es-szabalyok/afakulcsok#közösségi-áfa-euvat). |
| `marginVat` | `arresAfa` | Árrés alapú (különbözeti) áfa jelölése. |
| `paymentCorrection` | `fizetendoKorrekcio` | A fizetendő végösszeg korrekciója. |
| `logoExtra` | `logoExtra` | A fiókba feltöltött további logó neve. |
| `aggregator` | `aggregator` | Közvetítő (aggregátor) partner azonosítója. Alapból a kassza saját, a Számlázz.hu-tól kapott kódja; a sztornó és a befizetés is ezt küldi. |
| `guardian` | `guardian` | Partneri integrációkhoz használt jelző. |
| `articleIdentifierInvoice` | `cikkazoninvoice` | Cikkazonosító alapú számlázás jelzője. |
## Amit a kassza a küldés előtt ellenőriz [#amit-a-kassza-a-küldés-előtt-ellenőriz]
Ezeknél a hibáknál `validation` kategóriájú `SzamlazzError` jön, és kérés nem megy a Számlázz.hu-hoz:
- hiányzik a vevő neve, irányítószáma, települése vagy címe,
- nincs tétel, egy tételnek nincs neve vagy mennyiségi egysége, vagy a mennyisége 0,
- ismeretlen áfakulcs, vagy a `netUnitPrice` és a `grossUnitPrice` egyszerre, vagy egyik sincs megadva,
- a `dueDate` és a `paymentDueInDays` egyszerre szerepel, vagy egy dátum érvénytelen,
- forintos számlán árfolyam, devizás számlán árfolyam nélkül nem MNB bank,
- helyesbítő számla a helyesbített számla száma nélkül, végszámla előlegszámla-szám és rendelésszám nélkül,
- ismeretlen típus, nyelv vagy számlasablon,
- `sendEmail: true` e-mail cím nélkül, melléklet e-mail értesítő nélkül, 5-nél több vagy 2 MB-nál nagyobb melléklet,
- az egyszerűsített számlakép (`simpleItems`) szabályainak megsértése,
- hiányos MPL fuvarlevél adatok.
## Alapértékek a kliensben [#alapértékek-a-kliensben]
A gyakran ismétlődő mezőket nem kell minden hívásnál megadni. A `createKassza({ defaults: { invoice } })` alapértékei akkor érvényesülnek, ha a hívásban az adott mező hiányzik:
```ts
const kassza = createKassza({
defaults: {
invoice: {
prefix: 'WEB',
paymentDueInDays: 8,
language: 'hu',
seller: { bankAccount: '11773016-11111018-00000000', emailReplyTo: 'penzugy@example.hu' },
},
},
})
```
Az összes alapértelmezhető mezőt a [Kliens beállítása](/docs/alapok/kliens-beallitasa#alapértelmezések-számlára) oldal sorolja fel.
# Válasz
URL: https://www.kasszajs.hu/docs/szamla-letrehozas/valasz
> A CreatedInvoice mezői, honnan jönnek a Számlázz.hu válaszából, a PDF mentése, és a számlakészítésnél jellemző hibák.
Sikeres számlakészítés után a `create()` egy `CreatedInvoice` objektummal tér vissza:
```ts
const szamla = await kassza.invoices.create(adatok)
await db.rendeles.update({
where: { id: rendelesId },
data: { szamlaszam: szamla.number, brutto: szamla.grossTotal },
})
```
## CreatedInvoice [#createdinvoice]
| Mező | Típus | Forrás a válaszban | Leírás |
| --- | --- | --- | --- |
| `number` | `string` | `szamlaszam` | A kiállított számla száma, például `'WEB-2026-128'`. |
| `netTotal` | `number` | `szamlanetto` | Nettó végösszeg. |
| `grossTotal` | `number` | `szamlabrutto` | Bruttó végösszeg. |
| `outstanding` | `number \| undefined` | `kintlevoseg` | Hátralék. `paid: true` esetén `0`. |
| `buyerAccountUrl` | `string \| undefined` | `vevoifiokurl` | A vevői fiók linkje, ha a fiókban be van kapcsolva. Ezen keresztül a vevő online is fizethet. |
| `pdf` | `Uint8Array \| undefined` | `pdf` | A számla PDF-je. `downloadPdf: false` esetén nincs. |
| `items` | `CreatedInvoiceItem[]` | kassza | A tételek a kassza által számolt összegekkel. |
Egy `CreatedInvoiceItem` a tétel nevét és a számolt értékeket tartalmazza: `name`, `quantity`, `vat`, `netUnitPrice`, `netAmount`, `vatAmount`, `grossAmount`. Ezeket el is mentheted a saját adatbázisodba, pontosan ezek kerültek a számlára.
A kassza XML választ kér (`valaszVerzio` 2), amelyben a számlaszám, az összegek és a Base64
kódolású PDF is benne van. A válasz `szlahu_*` fejléceit is feldolgozza, így ha egy mező az
XML-ből hiányzik, a fejlécből pótolja.
## A PDF mentése [#a-pdf-mentése]
A `pdf` egy `Uint8Array`, amit közvetlenül fájlba írhatsz, feltölthetsz tárhelyre, vagy visszaadhatsz HTTP válaszként:
```ts
import { writeFile } from 'node:fs/promises'
if (szamla.pdf) await writeFile(`szamlak/${szamla.number}.pdf`, szamla.pdf)
```
```ts title="app/szamlak/[szam]/route.ts"
import { kassza } from '@/lib/kassza'
export async function GET(_request: Request, { params }: RouteContext<'/szamlak/[szam]'>) {
const { szam } = await params
const { pdf } = await kassza.invoices.getPdf(szam)
return new Response(new Uint8Array(pdf), {
headers: {
'Content-Type': 'application/pdf',
'Content-Disposition': `inline; filename="${szam}.pdf"`,
},
})
}
```
```ts
import { invoicePdfKey, s3FetchStorage, storePdf } from 'kassza/storage'
const tarhely = s3FetchStorage({
bucket: 'szamlak',
region: 'auto',
endpoint: process.env.R2_ENDPOINT,
accessKeyId: process.env.R2_ACCESS_KEY_ID ?? '',
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY ?? '',
})
if (szamla.pdf) await storePdf(tarhely, invoicePdfKey({ number: szamla.number }), szamla.pdf)
```
A PDF később is lekérhető a [Bizonylat lekérése PDF-ben](/docs/bizonylat-pdf) művelettel, ezért nem kötelező azonnal eltárolni.
## Előnézet válasza [#előnézet-válasza]
Az `invoices.preview()` ugyanazt a bemenetet fogadja, de nem állít ki számlát. `InvoicePreview` objektumot ad vissza, amelyben csak a `pdf`, a `netTotal`, a `grossTotal` és az `items` szerepel. Lásd az [Előnézet](/docs/szamla-letrehozas/beallitasok-es-szabalyok/elonezet) oldalt.
## Hibák [#hibák]
Ha a számla nem készült el, a `create()` `SzamlazzError`-t dob. A számlakészítésnél leggyakoribb hibák:
| Kód | Kategória | Mikor fordul elő? |
| --- | --- | --- |
| – | `validation` | A kassza a küldés előtt hibát talált a bemenetben, lásd [Kérés](/docs/szamla-letrehozas/keres#amit-a-kassza-a-küldés-előtt-ellenőriz). |
| 3 | `auth` | Hibás Agent kulcs. |
| 54, 55 | `account` | Az e-számla nincs engedélyezve, vagy az aláírás nem sikerült. |
| 56 | `partial_success` | A számla elkészült, csak az értesítő e-mail nem ment ki. **Ne állítsd ki újra.** |
| 57 | `validation` | Az XML nem felel meg az XSD-nek. |
| 71, 152 | `duplicate` | A rendelésszámmal már készült számla, és a fiók tiltja az ismétlést. |
| 136 | `account` | Lejárt előfizetés vagy rendezetlen díj. |
| 202 | `validation` | A számlaszám előtag nincs rögzítve a fiókban. |
| 259–264 | `validation` | Hibás tételösszeg. A kassza számolásával nem fordulhat elő, csak kézzel megadott összegeknél. |
| 352, 396 | `validation` | Érvénytelen kelte vagy túl korai dátum. |
| 537–539 | `validation`, `account` | Adattörlő kód hiba. |
| 551–556 | `validation` | Az egyszerűsített számlakép szabályainak megsértése. |
| – | `network`, `timeout` | Nem tudni, elkészült-e a számla. Kérdezd le rendelésszám alapján. |
Az összes kódot a [Hibakezelés, hibakódok](/docs/alapok/hibakezeles#hibakódok) oldal sorolja fel.
Hálózati hiba, időtúllépés, 56-os vagy 71/152-es hiba után a számla már létezhet. Mielőtt újra
kiállítanád, hívd meg a `kassza.invoices.find({ orderNumber })` metódust.
# Minta
URL: https://www.kasszajs.hu/docs/szamla-letrehozas/minta
> Futtatható számlakészítési példák a kassza kódjával, a ténylegesen elküldött xmlszamla XML-lel és a Számlázz.hu válaszával.
Az alábbi példák a [sandboxban](/sandbox) futottak. A **Kód** fül a kassza hívást, az **Elküldött XML** fül a Számla Agentnek küldött `xmlszamla` dokumentumot, a **Válasz** fül a szimulált Számlázz.hu választ mutatja. A kód fölé húzva az egeret a típusokat is látod.
## B2B számla nettó árakkal [#b2b-számla-nettó-árakkal]
Vállalkozásnak szóló számla, nettó egységárakkal, 8 napos fizetési határidővel. A vevőnek e-mail címe is van, ezért a Számlázz.hu elküldi neki a számlát.
## Webshop számla bruttó árakkal [#webshop-számla-bruttó-árakkal]
Magánszemélynek szóló, kártyával kifizetett rendelés. Bruttó egységárak, két különböző áfakulcs, a kliens alapbeállításaiban megadott előtaggal és e-mail szöveggel.
## XSD séma [#xsd-séma]
A kassza az XML-t a Számlázz.hu `xmlszamla.xsd` sémája szerint, kötött elemsorrendben építi fel. A séma itt érhető el:
```text
https://www.szamlazz.hu/szamla/docs/xsds/agent/xmlszamla.xsd
```
A kassza tesztjei a generált XML-t a hivatalos XSD sémákkal is összevetik, ezért a sémával neked nem kell foglalkoznod. Ha a hivatalos dokumentációban olyan mezőt találsz, amely a kasszában még nincs meg, [nyiss egy issue-t](https://github.com/futozs/kassza/issues).
## További példák [#további-példák]
Díjbekérő kiállítása és a befizetés utáni végleges számla.
Előlegszámla, majd végszámla az előleg levonásával.
Euróban kiállított angol nyelvű számla EU-s vevőnek.
Webhookból indított számlázás dupla számla nélkül.
# Számlázási beállítások és szabályok
URL: https://www.kasszajs.hu/docs/szamla-letrehozas/beallitasok-es-szabalyok
> Bizonylattípusok, áfakulcsok, kerekítés, devizák, számlakép és nyelv, rendelésszám, kedvezmény, e-mail értesítő, adattörlő kód és előnézet.
A számlakészítés szabályai: mit fogad el a Számla Agent, mit számol ki helyetted a kassza, és mire figyelj, hogy a számla elsőre elkészüljön.
Számla, díjbekérő, előleg-, vég- és helyesbítő számla, szállítólevél, e-számla.
A simpleItems mező, a korlátai és a NAV adatszolgáltatás.
Számként és kóddal megadható áfakulcsok, közösségi áfa.
Nettó és bruttó alapú számítás forintban és devizában, kalkulátorral.
Pénznemkódok, árfolyam és árfolyamot jegyző bank.
Számlaképek és a 15 számlanyelv.
Dupla számla elleni védelem rendelésszámmal.
Kedvezmény negatív összegű tételként.
Mikor megy e-mail, hogyan szabd testre, mellékletek.
A tételek torloKod mezője és a korlátai.
PDF előnézet kiállítás nélkül.
# Bizonylattípusok
URL: https://www.kasszajs.hu/docs/szamla-letrehozas/beallitasok-es-szabalyok/bizonylattipusok
> Számla, díjbekérő, előlegszámla, végszámla, helyesbítő számla és szállítólevél kiállítása a type mezővel, valamint e-számla vagy papírszámla.
Az `invoices.create()` a `type` mező alapján többféle bizonylatot készít. A sztornó számla kivétel, arra külön művelet van: [Számla sztornó](/docs/szamla-sztorno).
| `type` | Bizonylat | XML a `fejlec` blokkban | Kötelező kiegészítés |
| --- | --- | --- | --- |
| `'invoice'` | Számla (alapértelmezett) | – | – |
| `'proforma'` | Díjbekérő | `true` | – |
| `'advance'` | Előlegszámla | `true` | – |
| `'final'` | Végszámla | `true` | `advanceInvoiceNumber` vagy az előlegszámla `orderNumber`-je |
| `'corrective'` | Helyesbítő számla | `true` | `correctedInvoiceNumber` |
| `'deliveryNote'` | Szállítólevél | `true` | – |
A `CreateInvoiceInput` a `type` szerint szétválasztott típus: `type: 'corrective'` mellett a TypeScript már szerkesztés közben jelzi, ha a `correctedInvoiceNumber` lemaradt.
## Díjbekérő [#díjbekérő]
A díjbekérő fizetési felszólítás, jogilag nem számla, és nem kerül a NAV-hoz. Ha a vevő kifizette, állíts ki róla számlát, és a `proformaNumber` mezőben hivatkozz a díjbekérőre. A Számlázz.hu így összekapcsolja a kettőt.
A ki nem fizetett díjbekérőt ne sztornózd, hanem töröld a [Díjbekérő törlése](/docs/dijbekero-torlese) művelettel. Ha díjbekérőt vagy szállítólevelet próbálsz sztornózni, a kassza `validation` hibát ad, mert a Számlázz.hu ilyenkor csendben visszaadná az eredeti bizonylatot.
## Előlegszámla és végszámla [#előlegszámla-és-végszámla]
Előlegnél előbb `type: 'advance'` előlegszámlát állítasz ki, a teljesítéskor pedig `type: 'final'` végszámlát. A végszámlán az előleget negatív mennyiségű tétellel vonod le.
A Számlázz.hu-ban egy előlegszámlához csak egy végszámla tartozhat. Egy előlegből nem készíthető
több részszámla, és több előlegszámla sem vonható össze egy végszámlában.
A végszámlánál az `advanceInvoiceNumber` helyett az előlegszámla rendelésszáma is elég. Ha egyiket sem adod meg, a kassza a kérés előtt hibát dob.
## Helyesbítő számla [#helyesbítő-számla]
Ha egy kiállított számla tartalma változik, például a vevő visszaküld egy terméket, állíts ki helyesbítő számlát a `correctedInvoiceNumber` hivatkozással. A változást negatív mennyiségű tétellel jelzed.
Ha a számlát teljesen érvényteleníteni kell, helyesbítés helyett [sztornózd](/docs/szamla-sztorno). Egyszerűsített számlaképpel (`simpleItems`) kiállított számla nem helyesbíthető, csak sztornózható.
## Szállítólevél [#szállítólevél]
A `type: 'deliveryNote'` szállítólevelet készít. A fuvarlevél adatait a `waybill` mezőben adhatod meg, fuvarleveles számlaképhez pedig a `template: 'SzlaFuvarlevelesAlap'` sablont használd. A mezőket a [Kérés](/docs/szamla-letrehozas/keres#fuvarlevél-waybill) oldal sorolja fel.
```ts
await kassza.invoices.create({
type: 'deliveryNote',
template: 'SzlaFuvarlevelesAlap',
orderNumber: 'REND-1001',
buyer,
items,
waybill: {
courier: 'MPL',
mpl: { customerCode: '12345678', barcode: 'JJH30000000000000', weight: '1.25' },
},
})
```
## E-számla vagy papírszámla [#e-számla-vagy-papírszámla]
Az `eInvoice` mező dönti el, hogy elektronikus számla (`true`) vagy hagyományos, nyomtatható számla (`false`, ez az alapértelmezett) készül. E-számlához saját tanúsítvány kell, vagy a Számlázz.hu által biztosított tanúsítvány. Ha a fiókban nincs engedélyezve az e-számla, 54-es, ha az aláírás nem sikerül, 55-ös hibát kapsz.
```ts
const kassza = createKassza({ defaults: { invoice: { eInvoice: true } } })
```
# Egyszerűsített számlakép utazásszervezőknek
URL: https://www.kasszajs.hu/docs/szamla-letrehozas/beallitasok-es-szabalyok/utazasszervezok
> A simpleItems mező, amellyel az utazásszervezők ár- és áfaadatok nélküli számlaképet állíthatnak ki, a korlátaival és hibakódjaival.
A 2026. január 1-jén hatályba lépett szabály szerint az utazásszervezőknek nem kell feltüntetniük a számlaképen a különbözeti áfa alapját és összegét. A Számla Agent ezt a `simpleItems` mezővel támogatja: a számla egyszerűsített képpel készül el, a NAV-nak küldött adatszolgáltatás viszont teljes marad.
Az egyszerűsített számlakép kizárólag az Áfa tv. 210/A. §-a szerinti utazásszervezési
szolgáltatásra használható. Más tevékenységhez jogszerűtlen.
## Bekapcsolás [#bekapcsolás]
A Számlázz.hu felületén ez fiókszintű beállítás, az Agenten keresztül viszont bizonylatonként kapcsolható. Ha minden számlád ilyen, tedd a kliens alapbeállításai közé:
```ts
const szamla = await kassza.invoices.create({
simpleItems: true,
buyer,
items: [{ name: 'Horvát tengerparti út, 7 éj', grossUnitPrice: 389_000, vat: 'K.AFA' }],
})
const utazasiKassza = createKassza({ defaults: { invoice: { simpleItems: true } } })
```
A kassza a mezőt pontosan `simpleItems` néven írja az XML-be. Ez fontos, mert az elgépelt (`simpleitems`) mezőnevet a Számlázz.hu figyelmen kívül hagyhatja, és a számla normál képpel készülne el.
## Mi látszik a számlán? [#mi-látszik-a-számlán]
| Megjelenik | Nem jelenik meg |
| --- | --- |
| a tétel neve és megjegyzése | az egységár |
| a mennyiség | a nettó érték |
| a bruttó végösszeg | az áfakulcs és az áfa összege |
| a számla megjegyzése | az áfaösszesítő táblázat |
A különbözeti adózásra vonatkozó tájékoztató szöveg automatikusan rákerül a számlára. Kivétel a `'K.AFA'` áfakód: ekkor a tájékoztatót neked kell a számla `comment` mezőjébe írnod.
## Korlátok [#korlátok]
A kassza ezeket a szabályokat a küldés előtt ellenőrzi, és `validation` hibát ad, ha valamelyik sérül:
| Szabály | Számlázz.hu hibakód |
| --- | --- |
| Legfeljebb 2 tétel, végszámlán legfeljebb 4 (2 levonó és 2 új tétel) | 552 |
| Csak `0`, `5`, `18`, `27`, `'TAM'`, `'AAM'`, `'K.AFA'` vagy `'F.AFA'` áfakulcs | 553 |
| Helyesbítő számlán és szállítólevélen nem használható | 556 |
Ezeket csak a Számlázz.hu tudja ellenőrizni:
| Szabály | Hibakód |
| --- | --- |
| A fiókban nem lehet bekapcsolva az OSS adózás, és az eladónak magyar adószám kell | 551 |
| Egyszerűsített képpel kiállított számla nem helyesbíthető | 554 |
| A végszámla tételeinek áfakulcsa meg kell egyezzen az előlegszámláéval | 555 |
A `template` mező egyszerűsített számlaképnél hatástalan, a számla mindig az egyszerűsített nézettel készül.
## Bizonylattípusonként [#bizonylattípusonként]
| Bizonylat | Egyszerűsített kép |
| --- | --- |
| Számla, díjbekérő, díjbekérőből készült számla | Bizonylatonként bekapcsolható |
| Előlegszámla | Bizonylatonként bekapcsolható |
| Végszámla | Az előlegszámla beállítását örökli |
| Sztornó számla | Az eredeti számla beállítását örökli |
| Helyesbítő számla, szállítólevél | Nem használható |
## NAV adatszolgáltatás [#nav-adatszolgáltatás]
A `simpleItems` csak a számlaképet változtatja meg. A NAV Online Számla rendszerébe mindig a teljes tételadat kerül, adóalappal és áfaösszeggel, ezért a számlakép és a NAV-nál látható adat eltérhet. Ez a funkció rendeltetésszerű működése.
# Áfakulcsok
URL: https://www.kasszajs.hu/docs/szamla-letrehozas/beallitasok-es-szabalyok/afakulcsok
> A vat mezőben megadható számszerű áfakulcsok és áfakódok, a VatRate típus, és a közösségi áfát jelző euVat mező.
Minden tételnél kötelező a `vat` mező. Értéke lehet szám (például `27`) vagy áfakód (például `'AAM'`). A típusa `VatRate`, így a szerkesztő felajánlja az elfogadott értékeket, a kassza pedig a küldés előtt elutasítja az ismeretlen kulcsot.
```ts
items: [
{ name: 'Könyv', grossUnitPrice: 4_990, vat: 5 },
{ name: 'Tanácsadás', netUnitPrice: 20_000, vat: 27 },
{ name: 'Oktatás, alanyi mentes', netUnitPrice: 15_000, vat: 'AAM' },
]
```
Az áfakulcs kiválasztása adózási kérdés, a kassza ebben nem dönthet helyetted. Egyeztess a
könyvelőddel, vagy olvasd el a Számlázz.hu tudástárának áfakulcsokról szóló cikkét.
## Áfakódok [#áfakódok]
A kódos áfakulcsok áfa összege mindig 0, a kassza a bruttó értéket a nettóval egyezőre számolja.
A `VatRate` típus a számokat és a kódokat is tartalmazza. Ha az áfakulcs felhasználói bemenetből vagy adatbázisból jön, az `isVatRate()` futásidőben is ellenőrzi:
```ts
import { isVatRate } from 'kassza/money'
const beirt = termek.afakulcs === 'AAM' ? 'AAM' : Number(termek.afakulcs)
if (!isVatRate(beirt)) throw new Error(`Ismeretlen áfakulcs: ${termek.afakulcs}`)
```
A lehetséges értékek listája `NUMERIC_VAT_RATES` és `SPECIAL_VAT_CODES` néven is elérhető, például egy legördülő menühöz.
## Közösségi áfa (euVat) [#közösségi-áfa-euvat]
Az `euVat: true` (XML-ben `eusAfa`) azt jelzi, hogy a számla nem tartalmaz magyar áfát, ezért a Számlázz.hu nem küld róla adatot a NAV Online Számla rendszerébe. Akkor használható, ha a céged OSS-regisztrált, vagy nem magyar adószámmal rendelkezik.
```ts
await kassza.invoices.create({
euVat: true,
currency: 'EUR',
buyer: { name: 'Jan Novák', country: 'Czechia', zip: '11000', city: 'Praha', address: 'Národní 1' },
items: [{ name: 'E-book', grossUnitPrice: 12.99, vat: 21 }],
})
```
A tételeknél közösségi áfa esetén is a megfelelő kulcsot vagy kódot kell megadnod (például
`'EUT'`, `'EUFAD37'`, vagy a vevő országának áfakulcsát). Téves adatszolgáltatás utólag nem
javítható, mert az a számla elkészültekor megtörténik.
# Kerekítés
URL: https://www.kasszajs.hu/docs/szamla-letrehozas/beallitasok-es-szabalyok/kerekites
> Hogyan számolja a kassza a tételek nettó, áfa és bruttó értékét forintos és devizás számlán, nettó és bruttó alapon, interaktív kalkulátorral.
A Számlázz.hu nem számol a tételadatokból: a nettó, az áfa és a bruttó értéket is a kérésnek kell tartalmaznia, és ha ezek nem stimmelnek, a számla 259–264-es hibával elbukik. A kasszában ezt nem kell kézzel kiszámolnod. Megadod az egységárat és az áfakulcsot, a többit a kassza számolja a hivatalos kerekítési szabályok szerint.
## Nettó vagy bruttó alapon? [#nettó-vagy-bruttó-alapon]
| | Nettó alapú | Bruttó alapú |
| --- | --- | --- |
| Mező | `netUnitPrice` | `grossUnitPrice` |
| Mikor? | Vállalkozásnak (B2B), nettó árlistával | Magánszemélynek (B2C), webshopban, bruttó árakkal |
| Ami biztosan pontos | A nettó érték | A bruttó érték, amit a vevő kifizet |
Egy tételnél csak az egyiket adhatod meg, a kettő együtt `validation` hibát ad.
## Forintos számla [#forintos-számla]
Forintos számlán csak a nettó egységár lehet tört szám, a tétel nettó, áfa és bruttó értéke egész.
**Nettó alapon** a kassza így számol:
1. nettó érték = nettó egységár × mennyiség, egészre kerekítve,
2. áfa érték = nettó érték × áfakulcs / 100, egészre kerekítve,
3. bruttó érték = nettó érték + áfa érték.
Például 3 darab 500 Ft nettó egységárú könyv 27%-os áfával: nettó 1 500 Ft, áfa 405 Ft, bruttó 1 905 Ft.
**Bruttó alapon** fordítva:
1. bruttó érték = bruttó egységár × mennyiség, egészre kerekítve,
2. áfa érték = bruttó érték × áfakulcs / (100 + áfakulcs), egészre kerekítve,
3. nettó érték = bruttó érték − áfa érték,
4. a nettó egységárat a kassza visszaszámolja a nettó értékből.
Például 3 darab 500 Ft bruttó egységárú könyv 27%-os áfával: bruttó 1 500 Ft, áfa 318,89 Ft helyett 319 Ft, nettó 1 181 Ft, nettó egységár 393,67 Ft.
## Devizás számla [#devizás-számla]
Devizás számlán mindhárom érték legfeljebb 2 tizedesjegyre kerekedik, ugyanazzal a sorrenddel, mint forintban.
## Nyugta [#nyugta]
Forintos nyugtán a bruttó érték egész, a nettó és az áfa legfeljebb 2 tizedes, és a nettó és az áfa összege pontosan kiadja a bruttót. A részleteket a [nyugta szabályai](/docs/nyugta-letrehozas/beallitasok/tetelosszegek) írják le.
## Kerekítési mód [#kerekítési-mód]
A kassza kereskedelmi kerekítést használ: a fél értékek nulláról elfelé kerekednek (2,5 → 3, −2,5 → −3), és a lebegőpontos pontatlanságot 15 értékes jegyre normalizálja, így például a 1,005 is helyesen 1,01 lesz két tizedesre.
## Számolás a kassza nélkül [#számolás-a-kassza-nélkül]
A kerekítő függvények külön is elérhetők a `kassza/money` modulból, például ha a kosárban ugyanazt az összeget akarod mutatni, ami a számlára kerül:
```ts
import { calculateInvoiceItem, summarizeItems } from 'kassza/money'
const tetelek = [
calculateInvoiceItem({ quantity: 2, grossUnitPrice: 5_990, vat: 27 }),
calculateInvoiceItem({ grossUnitPrice: 3_500, vat: 5 }),
]
const { grossAmount, byVat } = summarizeItems(tetelek)
```
A `grossAmount` a fizetendő végösszeg, a `byVat` az áfakulcsonkénti bontás.
Lásd még: [Pénzszámítás](/docs/kiegeszitok/penzszamitas).
## Saját összegek [#saját-összegek]
Ha a tételösszegeket máshonnan kapod, például egy ERP-ből, a `netAmount`, `vatAmount` és `grossAmount` mezőkkel felülírhatod a számolást. Mindhármat meg kell adnod, és a kassza ilyenkor nem ellenőrzi őket, a Számlázz.hu viszont igen.
# Támogatott devizanemek
URL: https://www.kasszajs.hu/docs/szamla-letrehozas/beallitasok-es-szabalyok/penznemek
> A currency mezőben megadható pénznemkódok, az árfolyam és az árfolyamot jegyző bank megadása devizás számlán.
A számla pénznemét a `currency` mező adja meg (XML-ben `penznem`), alapértéke `'HUF'`. A forint `'HUF'` és `'Ft'` alakban is megadható.
## Devizás számla [#devizás-számla]
Nem forintos számlánál a Számlázz.hu az áfát forintban is kimutatja, ehhez árfolyam kell. A kassza szabályai:
| Mező | XML elem | Alapérték | Leírás |
| --- | --- | --- | --- |
| `currency` | `penznem` | `'HUF'` | A pénznem kódja. |
| `exchangeBank` | `arfolyamBank` | `'MNB'` | Az árfolyamot jegyző bank. |
| `exchangeRate` | `arfolyam` | – | Az árfolyam. Ha a bank `'MNB'`, elhagyható, és a Számlázz.hu az aznapi MNB árfolyamot használja. Más banknál kötelező. |
Forintos számlán árfolyam és bank nem adható meg, a kassza ilyenkor `validation` hibát ad.
```ts
await kassza.invoices.create({
currency: 'USD',
exchangeBank: 'OTP',
exchangeRate: 358.2,
buyer,
items: [{ name: 'Szoftverfejlesztés', netUnitPrice: 1_200, vat: 'HO' }],
})
```
Devizás számlán a tételösszegek 2 tizedesjegyre kerekednek, lásd [Kerekítés](/docs/szamla-letrehozas/beallitasok-es-szabalyok/kerekites#devizás-számla).
## Pénznemkódok [#pénznemkódok]
A `Currency` típus a Számla Agent által ismert kódokat kínálja fel, de tetszőleges szöveget is elfogad, ha a Számlázz.hu később új pénznemet vezet be.
Nyugtán a devizát ugyanígy, a `currency`, `exchangeRate` és `exchangeBank` mezőkkel adod meg, az
XML-ben ezek a `devizaarf` és `devizabank` elemekbe kerülnek.
# Számlasablon és nyelvek
URL: https://www.kasszajs.hu/docs/szamla-letrehozas/beallitasok-es-szabalyok/szamlasablon-es-nyelvek
> A számlakép kiválasztása a template mezővel, és a számla, az értesítő e-mail és a vevői fiók nyelvének beállítása a language mezővel.
## Számlasablon [#számlasablon]
A `template` mező (XML-ben `szamlaSablon`) a számlaképet választja ki. Ha nem adod meg, a fiókban beállított alapértelmezett sablon készül.
| `template` | Számlakép |
| --- | --- |
| `'SzlaMost'` | A Számlázz.hu ajánlott számlaképe |
| `'SzlaAlap'` | Tradicionális számlakép |
| `'SzlaNoEnv'` | Borítékbarát számlakép, ablakos borítékhoz |
| `'Szla8cm'` | Hőnyomtatós számlakép, 8 cm széles |
| `'SzlaTomb'` | Retró kéziszámla |
| `'SzlaFuvarlevelesAlap'` | Fuvarleveles számlakép, lásd [Szállítólevél](/docs/szamla-letrehozas/beallitasok-es-szabalyok/bizonylattipusok#szállítólevél) |
```ts
const kassza = createKassza({ defaults: { invoice: { template: 'SzlaNoEnv' } } })
```
Egyszerűsített számlaképnél (`simpleItems: true`) a sablon hatástalan. Nyugtánál a PDF sablont a nyugta `template` mezője választja ki, lásd [Nyugta PDF sablon](/docs/nyugta-letrehozas/beallitasok/pdf-sablon).
## Nyelvek [#nyelvek]
A `language` mező (XML-ben `szamlaNyelve`) a számla nyelvét adja meg, alapértéke `'hu'`. A nyelv a számlán kívül az értesítő e-mail szövegét és a vevői fiók oldalának nyelvét is meghatározza.
| `language` | Nyelv | `language` | Nyelv |
| --- | --- | --- | --- |
| `'hu'` | magyar | `'cz'` | cseh |
| `'en'` | angol | `'pl'` | lengyel |
| `'de'` | német | `'bg'` | bolgár |
| `'it'` | olasz | `'nl'` | holland |
| `'ro'` | román | `'ru'` | orosz |
| `'sk'` | szlovák | `'si'` | szlovén |
| `'hr'` | horvát | `'fr'` | francia |
| `'es'` | spanyol | | |
A cseh nyelv kódja `'cz'`, a szlovéné `'si'`, nem az ISO szerinti `cs` és `sl`. A kassza típusa
ezt ismeri, ezért a szerkesztő jelzi, ha a szabványos kódot írnád.
Ha a vevő nyelvét a böngészőből olvasod ki, képezd le a Számlázz.hu kódjaira:
```ts
import type { InvoiceLanguage } from 'kassza'
const NYELVEK: Record = { cs: 'cz', sl: 'si', de: 'de', en: 'en' }
const language = NYELVEK[vevo.nyelv] ?? 'hu'
```
# Rendelésszám és duplikáció-ellenőrzés
URL: https://www.kasszajs.hu/docs/szamla-letrehozas/beallitasok-es-szabalyok/rendelesszam
> Az orderNumber mező, a Számlázz.hu „Rendelésszám ismétlődés tiltása” beállítása, és hogyan véd a dupla számla ellen a kasszával.
Az `orderNumber` (XML-ben `rendelesSzam`) a saját rendszered rendelésazonosítója. Opcionális, de **mindig add meg**: ezzel kérdezheted le később a számlát, és ez véd a dupla számla ellen.
```ts
await kassza.invoices.create({ orderNumber: `WEB-${rendeles.id}`, buyer, items })
const szamla = await kassza.invoices.find({ orderNumber: `WEB-${rendeles.id}` })
```
## Rendelésszám ismétlődés tiltása [#rendelésszám-ismétlődés-tiltása]
A Számlázz.hu fiókban, a Beállítások → Fiók beállításai → Számlázás beállítások menüben a „Rendelésszám ismétlődés tiltása” kapcsolóval tilthatod, hogy ugyanaz a rendelésszám több számlán szerepeljen. Nyugtára külön kapcsoló vonatkozik.
Bekapcsolt tiltásnál:
- egy bizonylattípuson belül egy rendelésszám csak egyszer használható, az ismételt kérés 71-es vagy 152-es hibát (`duplicate`) kap,
- az ellenőrzés típusonként történik, így egy számlán és egy nyugtán szerepelhet ugyanaz a rendelésszám,
- a sztornó és a helyesbítő számla kivétel, és sztornózás után a rendelésszám újra felhasználható.
## Véletlen dupla beküldés [#véletlen-dupla-beküldés]
Bekapcsolt tiltás mellett a Számlázz.hu nem hibát ad, hanem **a korábbi számlát adja vissza sikeres válaszként**, ha az ismételt kérésben minden egyezik:
- a vevő neve,
- a bruttó végösszeg,
- a kelte, a teljesítés dátuma és a fizetési határidő,
- és a korábbi számla az elmúlt 2 napban készült.
Ha bármelyik eltér, 71-es vagy 152-es hiba jön.
## A kassza ajánlott mintája [#a-kassza-ajánlott-mintája]
A fiók beállításától függetlenül működik, ha a számlakészítés előtt és bizonytalan hiba után is rákeresel a rendelésszámra:
```ts
import { isSzamlazzError } from 'kassza'
async function szamlaz(rendeles: Rendeles) {
const orderNumber = `WEB-${rendeles.id}`
const meglevo = await kassza.invoices.find({ orderNumber })
if (meglevo) return meglevo.header.number
try {
return (await kassza.invoices.create({ orderNumber, buyer: rendeles.vevo, items: rendeles.tetelek })).number
} catch (error) {
if (isSzamlazzError(error) && ['network', 'timeout', 'partial_success', 'duplicate'].includes(error.category)) {
const letrejott = await kassza.invoices.find({ orderNumber })
if (letrejott) return letrejott.header.number
}
throw error
}
}
```
Ha egy rendeléshez szándékosan több számla készül (például részszállításnál), a tiltást
kapcsold ki, és a rendelésszámot egészítsd ki sorszámmal: `WEB-5001-1`, `WEB-5001-2`.
Ha több számla tartozik ugyanahhoz a rendelésszámhoz, a `find()` és a `get()` a legutóbbit adja vissza.
# Kedvezmény feltüntetése a számlán
URL: https://www.kasszajs.hu/docs/szamla-letrehozas/beallitasok-es-szabalyok/kedvezmeny
> Kedvezmény megadása negatív egységárú tételként, a hozzá tartozó áfakulccsal, százalékos kedvezmény kiszámolása a kasszával.
A Számla Agentben nincs külön kedvezmény mező. A kedvezményt egy újabb, **negatív egységárú tételként** adod meg:
- a `name` a kedvezmény megnevezése vagy jogcíme,
- a `netUnitPrice` vagy a `grossUnitPrice` negatív, a `quantity` pozitív marad,
- a `vat` ugyanaz, mint annak a tételnek az áfakulcsa, amelyre a kedvezmény vonatkozik,
- ha a kedvezmény egy tételhez tartozik, közvetlenül utána szerepeljen.
```ts
await kassza.invoices.create({
buyer,
items: [
{ name: 'Minta termék', netUnitPrice: 20_000, vat: 27 },
{ name: 'Kedvezmény: Minta termék', netUnitPrice: -2_000, vat: 27 },
],
})
```
A kassza a negatív tételt ugyanúgy számolja, mint a többit: a fenti kedvezmény nettó −2 000 Ft, áfa −540 Ft, bruttó −2 540 Ft lesz.
## Százalékos és kosárkedvezmény [#százalékos-és-kosárkedvezmény]
Százalékos vagy végösszegből adott kedvezmény funkció a Számlázz.hu-ban nincs, az összeget neked kell kiszámolnod. Kosárkedvezménynél áfakulcsonként egy-egy kedvezménytételt adj meg, hogy az áfa is helyesen csökkenjen:
```ts
import type { InvoiceItemInput } from 'kassza'
import { roundMoney } from 'kassza/money'
function kedvezmennyel(tetelek: InvoiceItemInput[], szazalek: number): InvoiceItemInput[] {
const kulcsonkent = new Map()
for (const tetel of tetelek) {
const brutto = (tetel.grossUnitPrice ?? 0) * (tetel.quantity ?? 1)
kulcsonkent.set(tetel.vat, (kulcsonkent.get(tetel.vat) ?? 0) + brutto)
}
const kedvezmenyek = [...kulcsonkent].map(([vat, brutto]) => ({
name: `Kuponkedvezmény ${szazalek}%`,
grossUnitPrice: -roundMoney((brutto * szazalek) / 100),
vat,
}))
return [...tetelek, ...kedvezmenyek]
}
```
A példa bruttó egységárú tételekkel számol. Nettó áras tételeknél ugyanígy a nettó összegből
számold a kedvezményt, és `netUnitPrice`-ként add meg.
# Számlaértesítő küldés
URL: https://www.kasszajs.hu/docs/szamla-letrehozas/beallitasok-es-szabalyok/ertesito-email
> Mikor küld e-mailt a Számlázz.hu a vevőnek, hogyan kapcsold ki, hogyan szabd testre a tárgyat és a szöveget, és hogyan csatolj fájlokat.
Ha a vevőnek van e-mail címe (`buyer.email`), a Számlázz.hu az elkészült számlát elküldi neki. Ezt a `buyer.sendEmail: false` kapcsolja ki.
| `buyer.email` | `buyer.sendEmail` | Megy e-mail? |
| --- | --- | --- |
| nincs | nincs megadva | Nem |
| van | nincs megadva | **Igen** |
| van | `true` | Igen |
| van | `false` | Nem |
| nincs | `true` | A kassza `validation` hibát ad |
```ts
await kassza.invoices.create({
buyer: { ...vevo, email: 'vevo@example.hu', sendEmail: false },
items,
})
```
Több címzettet vesszővel választhatsz el: `email: 'penzugy@example.hu,konyveles@example.hu'`. Az alapértelmezést a `defaults.invoice.sendEmail` is beállíthatja, de a hívásban megadott érték mindig erősebb.
Tesztfiókban az értesítő nem a vevő címére megy, hanem a fiók beállításaiban megadott e-mail
címre, így fejlesztés közben nem írhatsz véletlenül valódi vevőnek.
## Tárgy és szöveg [#tárgy-és-szöveg]
Az e-mail tárgyát, szövegét és válaszcímét az eladó (`seller`) mezőiben adhatod meg. A szövegben sortörést egyszerű új sorral, formázást BB kódokkal kérhetsz:
| BB kód | Hatás |
| --- | --- |
| `[b]…[/b]` | félkövér |
| `[i]…[/i]` | dőlt |
| `[u]…[/u]` | aláhúzott |
| `[h1]…[/h1]` – `[h6]…[/h6]` | címsor |
| `[center]…[/center]` | középre igazított |
```ts
const kassza = createKassza({
defaults: {
invoice: {
seller: {
emailReplyTo: 'hello@example.hu',
emailSubject: 'Megérkezett a számlád',
emailText: '[b]Köszönjük a vásárlást![/b]\nA számládat csatoltuk.\n\nÜdv,\nA csapat',
},
},
},
})
```
## Mellékletek [#mellékletek]
Az értesítőhöz legfeljebb 5 fájlt csatolhatsz, fájlonként legfeljebb 2 MB-ot. A kassza ezt, és azt is ellenőrzi, hogy e-mail valóban menni fog, mert értesítő nélkül a Számlázz.hu a mellékleteket figyelmen kívül hagyja.
```ts
import { readFile } from 'node:fs/promises'
await kassza.invoices.create({
buyer: { ...vevo, email: 'vevo@example.hu' },
items,
attachments: [
{ filename: 'aszf.pdf', content: await readFile('aszf.pdf'), contentType: 'application/pdf' },
],
})
```
Ha egy melléklet hibás, a Számlázz.hu a számlakészítés beküldőjének figyelmeztető e-mailt küld, a vevő pedig a többi melléklettel együtt megkapja az értesítőt.
## Ha az e-mail nem ment ki [#ha-az-e-mail-nem-ment-ki]
Ha a számla elkészült, de az értesítő kiküldése nem sikerült, a kassza 56-os, `partial_success` kategóriájú hibát dob. Ilyenkor **ne állítsd ki újra a számlát**: a számla létezik, a `find({ orderNumber })` megtalálja.
# Adattörlő kód
URL: https://www.kasszajs.hu/docs/szamla-letrehozas/beallitasok-es-szabalyok/adattorlo-kod
> A tételek dataDeletionCode mezője (torloKod), a fiókbeállítás, a korlátok és a kapcsolódó hibakódok.
Minden tételhez megadható egy adattörlő kód a `dataDeletionCode` mezőben (XML-ben `torloKod`). Értéke 0 vagy annál nagyobb egész szám, a kassza a küldés előtt ezt ellenőrzi.
```ts
items: [
{ name: 'Minta termék', quantity: 2, netUnitPrice: 10_000, vat: 27, dataDeletionCode: 123 },
]
```
## Feltételek és hibák [#feltételek-és-hibák]
| Feltétel | Ha nem teljesül |
| --- | --- |
| A Számlázz.hu fiók beállításaiban be kell kapcsolni az adattörlő kód használatát | 539-es hiba (`account`) |
| Egy tételhez legfeljebb 400 adattörlő kód tartozhat | 537-es hiba (`validation`) |
| Demo- és tesztfiókban nem használható | 538-as hiba (`account`) |
A `torloKod` a hivatalos dokumentációban szerepel, de a letölthető `xmlszamla.xsd` sémában
(és a nyugta sémájában) nincs benne. Csak akkor használd, ha tényleg szükséged van rá, és ha
57-es hibát kapsz, hagyd el.
Nyugtán ugyanígy, a tételek `dataDeletionCode` mezőjével adhatod meg.
# Előnézet
URL: https://www.kasszajs.hu/docs/szamla-letrehozas/beallitasok-es-szabalyok/elonezet
> Számla PDF előnézet kiállítás nélkül az invoices.preview() metódussal, például megrendelés jóváhagyásához vagy sablon teszteléséhez.
Az `invoices.preview()` ugyanazt a bemenetet fogadja, mint a `create()`, de **nem állít ki számlát**: a Számlázz.hu elkészíti a PDF-et, a fiókban viszont nem jön létre bizonylat, számlaszám nem fogy, és a NAV-hoz sem megy adat.
```ts title="app/api/elonezet/route.ts"
import { kassza } from '@/lib/kassza'
export async function POST(request: Request) {
const rendeles = await request.json()
const elonezet = await kassza.invoices.preview({
buyer: rendeles.vevo,
items: rendeles.tetelek,
})
return new Response(new Uint8Array(elonezet.pdf), {
headers: { 'Content-Type': 'application/pdf' },
})
}
```
## Mire jó? [#mire-jó]
- A vevő a megrendelés elküldése előtt megnézheti, pontosan milyen számlát kap.
- Új számlasablont, nyelvet vagy e-mail beállítást próbálhatsz ki anélkül, hogy számlát kellene sztornóznod.
- Adminfelületen a könyvelő jóváhagyhatja a tételeket a kiállítás előtt.
## Különbségek a create()-hez képest [#különbségek-a-create-hez-képest]
| | `create()` | `preview()` |
| --- | --- | --- |
| Bizonylat a fiókban | Igen | Nem |
| Visszaadott típus | `CreatedInvoice` | `InvoicePreview` |
| Számlaszám | Van | Nincs |
| PDF | Ha `downloadPdf` nem `false` | Mindig |
| E-mail értesítő és mellékletek | Igen | Nem, a mellékleteket el sem küldi |
| Validáció és kerekítés | Igen | Ugyanaz |
A kassza előnézetnél az `elonezetpdf` elemet teszi a kérés fejlécébe, és mindig kéri a PDF-et. Az előnézetet sem próbálja újra magától, mert ugyanazt a műveletet hívja, mint a számlakészítés.
# Számla sztornó
URL: https://www.kasszajs.hu/docs/szamla-sztorno
> Kiállított számla érvénytelenítése sztornó számlával a kassza invoices.reverse() metódusával.
A kiállított számlát nem lehet törölni, csak sztornózni: a Számlázz.hu egy sztornó számlát készít, amely az eredeti számla összegeit ellentétes előjellel tartalmazza. A kasszában ezt az `invoices.reverse()` végzi.
```ts
const sztorno = await kassza.invoices.reverse('WEB-2026-128')
```
A sztornó mezői: dátumok, megjegyzés, e-mail, vevő adatai.
A ReversedInvoice mezői és a sztornónál előforduló hibák.
Számla kiállítása és sztornózása a sandboxban.
Díjbekérő és szállítólevél nem sztornózható. A ki nem fizetett díjbekérőt a
[Díjbekérő törlése](/docs/dijbekero-torlese) művelettel töröld.
# Kérés
URL: https://www.kasszajs.hu/docs/szamla-sztorno/keres
> Az invoices.reverse() bemenete, a Számla Agent xmlszamlast XML elemeivel és alapértékekkel.
```ts
kassza.invoices.reverse(input: string | ReverseInvoiceOptions, options?: { signal?: AbortSignal }): Promise
```
A kassza `xmlszamlast` XML-t küld az `action-szamla_agent_st` form mezőben. A legegyszerűbb hívás csak a sztornózandó számla számát kapja:
```ts
await kassza.invoices.reverse('WEB-2026-128')
```
Ha a sztornóhoz megjegyzést, más dátumot vagy saját e-mail szöveget adnál, objektumot adj át:
```ts
await kassza.invoices.reverse({
invoiceNumber: 'WEB-2026-128',
comment: 'A megrendelést a vevő lemondta.',
email: { subject: 'Sztornó számla', text: 'A korábbi számládat érvénytelenítettük.' },
})
```
## Mezők [#mezők]
| Mező | XML elem | Alapérték | Leírás |
| --- | --- | --- | --- |
| `invoiceNumber` | `fejlec/szamlaszam` | – | **Kötelező.** A sztornózandó számla száma. |
| `issueDate` | `fejlec/keltDatum` | Számlázz.hu alapértelmezés | A sztornó számla kelte. |
| `fulfillmentDate` | `fejlec/teljesitesDatum` | Számlázz.hu alapértelmezés | A sztornó teljesítési dátuma. |
| `comment` | `fejlec/megjegyzes` | – | Megjegyzés a sztornó számlán. |
| `template` | `fejlec/szamlaSablon` | fiók alapértelmezés | Számlakép, lásd [Számlasablon](/docs/szamla-letrehozas/beallitasok-es-szabalyok/szamlasablon-es-nyelvek). |
| `eInvoice` | `beallitasok/eszamla` | `false` | E-számla legyen-e a sztornó. |
| `downloadPdf` | `beallitasok/szamlaLetoltes` | `true` | Kérje-e a sztornó PDF-jét. |
| `externalId` | `beallitasok/szamlaKulsoAzon` | – | A sztornó számla külső azonosítója. |
| `email.replyTo` | `elado/emailReplyto` | – | Az értesítő válaszcíme. |
| `email.subject` | `elado/emailTargy` | – | Az értesítő tárgya. |
| `email.text` | `elado/emailSzoveg` | – | Az értesítő szövege. |
| `buyer.email` | `vevo/email` | – | Erre a címre megy a sztornó értesítő. |
| `buyer.taxNumber` | `vevo/adoszam` | – | A vevő adószáma, ha az eredeti számláról hiányzott. |
| `buyer.euTaxNumber` | `vevo/adoszamEU` | – | A vevő közösségi adószáma, ha az eredeti számláról hiányzott. |
A kassza a `fejlec/tipus` elembe mindig `SS` (sztornó számla) értéket ír.
## Szabályok [#szabályok]
- Díjbekérő és szállítólevél nem sztornózható. Ilyenkor a Számlázz.hu nem hibát ad, hanem visszaküldi az eredeti bizonylatot. A kassza ezt felismeri, és `validation` hibát dob.
- Sztornózás után a számla rendelésszáma újra felhasználható, akkor is, ha a fiókban tiltva van a rendelésszám ismétlődése.
- Egyszerűsített számlaképpel (`simpleItems`) kiállított számla sztornója is egyszerűsített képpel készül.
- A kassza a sztornót **nem próbálja újra** magától, mert egy második sztornó kérés hibát vagy duplikációt okozhat.
# Válasz
URL: https://www.kasszajs.hu/docs/szamla-sztorno/valasz
> A ReversedInvoice mezői, amelyeket az invoices.reverse() visszaad, és a sztornózásnál jellemző hibák.
Sikeres sztornó után a `reverse()` egy `ReversedInvoice` objektumot ad vissza. A mezők a Számlázz.hu `xmlszamlavalasz` válaszából és a `szlahu_*` fejlécekből jönnek.
| Mező | Típus | Forrás | Leírás |
| --- | --- | --- | --- |
| `number` | `string` | `szamlaszam` | A sztornó számla száma. |
| `netTotal` | `number \| undefined` | `szamlanetto` | A sztornó nettó végösszege, negatív. |
| `grossTotal` | `number \| undefined` | `szamlabrutto` | A sztornó bruttó végösszege, negatív. |
| `outstanding` | `number \| undefined` | `kintlevoseg` | Hátralék. |
| `buyerAccountUrl` | `string \| undefined` | `vevoifiokurl` | A vevői fiók linkje. |
| `pdf` | `Uint8Array \| undefined` | `pdf` | A sztornó számla PDF-je. |
```ts
const sztorno = await kassza.invoices.reverse('WEB-2026-128')
if (sztorno.pdf) await writeFile(`sztorno/${sztorno.number}.pdf`, sztorno.pdf)
```
Hogy egy számla sztornózva van-e, a [Számla adatainak lekérése](/docs/szamla-adatai) után a `header.reversed` mező mutatja.
## Hibák [#hibák]
| Kód | Kategória | Mikor fordul elő? |
| --- | --- | --- |
| – | `validation` | Hiányzó számlaszám, érvénytelen dátum vagy ismeretlen sablon. |
| – | `validation` | Díjbekérőt vagy szállítólevelet próbáltál sztornózni: a Számlázz.hu az eredeti bizonylatot adta vissza. |
| 3 | `auth` | Hibás Agent kulcs. |
| 7 | `not_found` | Nincs ilyen számlaszám. |
| 54, 55 | `account` | E-számla sztornó, de az e-számla nincs engedélyezve, vagy az aláírás nem sikerült. |
| 56 | `partial_success` | A sztornó elkészült, de az értesítő nem ment ki. **Ne küldd újra.** |
| – | `network`, `timeout` | Nem tudni, elkészült-e a sztornó. Ellenőrizd a `get()` metódussal, mielőtt újra próbálnád. |
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.reverse(szamlaszam)
} catch (error) {
if (isSzamlazzError(error) && (error.category === 'network' || error.category === 'timeout')) {
const adatok = await kassza.invoices.get(szamlaszam)
if (adatok.header.reversed) return
}
throw error
}
```
# Minta
URL: https://www.kasszajs.hu/docs/szamla-sztorno/minta
> Számla kiállítása, sztornózása és a sztornózott állapot ellenőrzése, a ténylegesen elküldött xmlszamlast XML-lel.
A példa kiállít egy számlát, sztornózza, majd lekérdezi az eredeti számla adatait, hogy a `header.reversed` mező már `true`. Az **Elküldött XML** fülön mindhárom kérést látod, köztük a sztornó `xmlszamlast` dokumentumát.
## XSD séma [#xsd-séma]
```text
https://www.szamlazz.hu/szamla/docs/xsds/agentst/xmlszamlast.xsd
```
# Befizetés rögzítése
URL: https://www.kasszajs.hu/docs/befizetes-rogzitese
> Befizetés (jóváírás) rögzítése és törlése kiállított számlán az invoices.registerPayment() és clearPayments() metódussal, és a fizetési értesítés (IPN) fogadása.
Ha a vevő nem a számla kiállításakor fizet, hanem később, átutalással, részletekben vagy készpénzben, a befizetést utólag rögzítheted a számlán. A Számlázz.hu a befizetésekből számolja a számla hátralékát. A kasszában az `invoices.registerPayment()` rögzít befizetést, az `invoices.clearPayments()` pedig törli a számla összes befizetését.
A másik irányt az IPN kezeli: ha a Számlázz.hu-ban megváltozik egy számla kifizetett összege, értesítést küld a webhook végpontodra, amelyet a `kassza/ipn` modullal dolgozol fel.
```ts
const befizetes = await kassza.invoices.registerPayment({
invoiceNumber: 'WEB-2026-128',
amount: 19_470,
method: 'bankkártya',
})
console.log(befizetes.outstanding)
```
Egy vagy több befizetés, hozzáadás vagy felülírás, törlés és újrapróbálás.
A RegisteredPayment mezői és a befizetésnél előforduló hibák.
Számla, részletfizetés és törlés a sandboxban, az elküldött XML-lel.
Webhook a Számlázz.hu kifizetési értesítéséhez, IP-ellenőrzéssel.
A `registerPayment()` alapból hozzáadja a befizetést a korábbiakhoz. Ha ugyanazt a hívást kétszer
küldöd el, a számlán kétszer szerepel a befizetés. Ezért a kassza ezt a hívást nem próbálja újra
magától, lásd [Újrapróbálás](/docs/befizetes-rogzitese/keres#újrapróbálás).
## Befizetés pontosan egyszer [#befizetés-pontosan-egyszer]
Ha ugyanazt a befizetést több helyről is rögzítheted (banki értesítés, webhook, kézi egyeztetés), vagy egy hívás időtúllépéssel végződött, használd a `registerPaymentOnce()` metódust egy stabil kulccsal, például a banki tranzakció azonosítójával:
```ts
const eredmeny = await kassza.invoices.registerPaymentOnce({
invoiceNumber: 'WEB-2026-128',
key: 'BANK-20261008-4411',
amount: 19_470,
method: 'átutalás',
})
console.log(eredmeny.created)
```
A kassza a befizetés leírásába egy rövid jelölőt tesz (`[kassza:BANK-20261008-4411]`), és rögzítés előtt megnézi, van-e már ilyen jelölésű befizetés a számlán. Ha van, nem rögzít újra (`created: false`). Időtúllépés után ugyanígy visszakeres. A kulcs legfeljebb 48 karakter lehet, betűkből, számokból és `_ . : -` jelekből.
A rögzítés után a kassza ellenőrzi, hogy a jelölő látszik-e a számla lekérdezésében. Ha nem, nem
hallgat el: `unexpected_response` hibát dob `registered: 'true'` jelöléssel. Ilyenkor a befizetés
már rögzült, ne hívd újra, és jelezd a kassza hibajegyében.
# Kérés
URL: https://www.kasszajs.hu/docs/befizetes-rogzitese/keres
> Az invoices.registerPayment() és clearPayments() bemenete, a Számla Agent xmlszamlakifiz XML elemeivel, alapértékekkel és az újrapróbálás szabályával.
```ts
kassza.invoices.registerPayment(input: RegisterPaymentInput, options?: { signal?: AbortSignal }): Promise
kassza.invoices.clearPayments(input: string | { invoiceNumber: string; taxNumber?: string }, options?: { signal?: AbortSignal }): Promise
```
A kassza mindkét metódusnál `xmlszamlakifiz` XML-t küld az `action-szamla_agent_kifiz` form mezőben. A `registerPayment()` kétféle bemenetet fogad: egyetlen befizetést közvetlenül a mezőkkel, vagy legfeljebb öt befizetést a `payments` tömbben.
## Egy befizetés [#egy-befizetés]
```ts
await kassza.invoices.registerPayment({
invoiceNumber: 'WEB-2026-128',
amount: 19_470,
})
```
Ez a hívás mai dátummal, `'átutalás'` jogcímmel rögzít egy 19 470 összegű befizetést, a számla korábbi befizetései mellé. Ha mást akarsz, add meg a `method`, a `date` vagy a `description` mezőt.
## Több befizetés egy kérésben [#több-befizetés-egy-kérésben]
```ts
await kassza.invoices.registerPayment({
invoiceNumber: 'WEB-2026-128',
payments: [
{ date: '2026-09-01', method: 'készpénz', amount: 10_000 },
{ date: '2026-09-15', method: 'átutalás', amount: 9_470, description: 'Második részlet' },
],
})
```
A tömbös formában a `method` minden elemnél kötelező, itt nincs alapértéke. Egy kérésben legfeljebb 5 befizetés lehet.
## Mezők [#mezők]
| Mező | XML elem | Alapérték | Leírás |
| --- | --- | --- | --- |
| `invoiceNumber` | `beallitasok/szamlaszam` | – | **Kötelező.** A számla száma, amelyhez a befizetés tartozik. |
| `taxNumber` | `beallitasok/adoszam` | – | A számla kiállítójának, vagyis a saját cégednek az adószáma. A Számlázz.hu ezzel is azonosítja, melyik számlához tartozik a befizetés. |
| `additive` | `beallitasok/additiv` | `true` | `true` esetén az új befizetések a korábbiak mellé kerülnek. `false` esetén a kérésben küldött befizetések lecserélik a korábbiakat. |
| `amount` | `kifizetes/osszeg` | – | **Kötelező.** A befizetett összeg. Véges szám kell legyen. |
| `method` | `kifizetes/jogcim` | `'átutalás'` egy befizetésnél | A befizetés jogcíme, szabad szöveg, például `'készpénz'` vagy `'bankkártya'`. A `payments` tömbben kötelező. |
| `date` | `kifizetes/datum` | ma | A befizetés dátuma. `Date` vagy `'YYYY-MM-DD'`. |
| `description` | `kifizetes/leiras` | – | Leírás a befizetéshez. Üres szöveg esetén a kassza kihagyja az elemet. |
| `payments` | `kifizetes` (legfeljebb 5) | – | Több befizetés egy kérésben. Egy elem mezői: `amount`, `method`, `date`, `description`. |
A „ma” mindig a budapesti naptári nap. `Date` objektumnál is a budapesti idő szerinti dátum kerül az XML-be, így például a `2026-09-15T23:30:00Z` időpontból `2026-09-16` lesz. A kassza a `beallitasok/valaszVerzio` elembe mindig `2`-t ír, ezért a Számlázz.hu XML-ben válaszol.
## Korábbi befizetések felülírása [#korábbi-befizetések-felülírása]
`additive: false` esetén a számlán a hívás után pontosan a kérésben küldött befizetések szerepelnek. Ha a befizetéseket a saját adatbázisodban is vezeted, így egy lépésben szinkronizálhatod őket:
```ts
await kassza.invoices.registerPayment({
invoiceNumber: 'WEB-2026-128',
additive: false,
payments: [
{ date: '2026-09-01', method: 'készpénz', amount: 10_000 },
{ date: '2026-09-15', method: 'átutalás', amount: 9_470 },
],
})
```
Ennek a formának az eredménye ugyanaz, akárhányszor ér be a kérés, ezért a kassza hálózati hiba után újra is próbálja.
## Befizetések törlése [#befizetések-törlése]
A `clearPayments()` a számla összes befizetését törli. Elég a számlaszámot megadni, de objektumban az adószámot is átadhatod:
```ts
await kassza.invoices.clearPayments('WEB-2026-128')
await kassza.invoices.clearPayments({ invoiceNumber: 'WEB-2026-128', taxNumber: '12345676-1-42' })
```
A kassza ilyenkor `additiv` `false` értékkel, `kifizetes` elem nélkül küldi a kérést. Üres `payments` tömbbel a `registerPayment()` nem töröl, hanem `validation` hibát dob, és a `clearPayments()` metódusra irányít, így véletlenül nem tüntetheted el a befizetéseket.
## Újrapróbálás [#újrapróbálás]
A kassza csak a felülíró hívásokat próbálja újra: a `registerPayment()` hívást `additive: false` mellett, és a `clearPayments()` hívást. Ezeknél egy második beérkezés ugyanazt az állapotot hozza létre. Az alapértelmezett, hozzáadó hívást soha nem küldi újra, mert egy ismételt kérés dupla befizetést rögzítene.
Újrapróbálás is csak `maintenance`, `network` és `timeout` hibánál történik, alapból legfeljebb háromszor, 1, majd 2 másodperc várakozással. A részleteket a [Hibakezelés](/docs/alapok/hibakezeles#amit-a-kassza-újrapróbál) oldal írja le. Mit tegyél, ha egy hozzáadó hívás bizonytalan kimenettel ért véget, azt a [Válasz](/docs/befizetes-rogzitese/valasz#hibák) oldalon találod.
## Amit a kassza a küldés előtt ellenőriz [#amit-a-kassza-a-küldés-előtt-ellenőriz]
Ezeknél a hibáknál `validation` kategóriájú `SzamlazzError` jön, és kérés nem megy a Számlázz.hu-hoz:
- hiányzik vagy üres a számlaszám,
- a `payments` tömb üres, vagy 5-nél több elemet tartalmaz,
- egy befizetés összege nem véges szám, például `NaN`,
- egy befizetésnél hiányzik vagy üres a `method`,
- egy dátum érvénytelen.
# Válasz
URL: https://www.kasszajs.hu/docs/befizetes-rogzitese/valasz
> A RegisteredPayment mezői, amelyeket a registerPayment() és a clearPayments() visszaad, és a befizetés rögzítésénél jellemző hibák.
Sikeres kérés után a `registerPayment()` és a `clearPayments()` is egy `RegisteredPayment` objektumot ad vissza. A mezők a Számlázz.hu `xmlszamlavalasz` válaszából jönnek. Ha egy elem hiányzik az XML-ből, a kassza a `szlahu_*` fejlécekből pótolja.
| Mező | Típus | Forrás | Leírás |
| --- | --- | --- | --- |
| `invoiceNumber` | `string` | `szamlaszam` | A számla száma. Ha a válaszban nincs benne, a kérésben megadott szám. |
| `netTotal` | `number \| undefined` | `szamlanetto` | A számla nettó végösszege. |
| `grossTotal` | `number \| undefined` | `szamlabrutto` | A számla bruttó végösszege. |
| `outstanding` | `number \| undefined` | `kintlevoseg` | A számla hátraléka a befizetések után. |
| `buyerAccountUrl` | `string \| undefined` | `vevoifiokurl` | A vevői fiók linkje, ha a fiókban be van kapcsolva. |
A válaszban nincs PDF, és a rögzített befizetések listája sem. A hátralékból viszont látod, hogy a számla ki van-e egyenlítve:
```ts
const eredmeny = await kassza.invoices.registerPayment({ invoiceNumber: szamlaszam, amount: 19_470 })
if (eredmeny.outstanding === 0) {
await db.rendeles.update({ where: { szamlaszam }, data: { fizetve: true } })
}
```
A számlán szereplő befizetéseket a [Számla adatainak lekérése](/docs/szamla-adatai) adja vissza:
a `kassza.invoices.get()` eredményének `payments` tömbjében minden befizetés dátuma (`date`),
jogcíme (`method`) és összege (`amount`) benne van.
## Hibák [#hibák]
| Kód | Kategória | Mikor fordul elő? |
| --- | --- | --- |
| – | `validation` | A kassza a küldés előtt hibát talált, lásd [Kérés](/docs/befizetes-rogzitese/keres#amit-a-kassza-a-küldés-előtt-ellenőriz). |
| 1 | `maintenance` | Karbantartás. Felülíró hívásnál a kassza magától újrapróbálja. |
| 3 | `auth` | Hibás Agent kulcs. |
| 7 | `not_found` | Ismeretlen számlaszám. |
| 136 | `account` | Lejárt előfizetés vagy rendezetlen díj. |
| – | `unexpected_response` | A Számlázz.hu ismeretlen formátumban válaszolt. |
| – | `network`, `timeout` | Nem tudni, rögzült-e a befizetés. Hozzáadó hívásnál ellenőrizd, mielőtt újra küldenéd. |
Az összes kódot a [Hibakezelés, hibakódok](/docs/alapok/hibakezeles#hibakódok) oldal sorolja fel.
## Bizonytalan kimenet hozzáadó hívásnál [#bizonytalan-kimenet-hozzáadó-hívásnál]
Hálózati hiba vagy időtúllépés után a befizetés már rögzülhetett. Hozzáadó hívásnál egy vak újraküldés dupla befizetést okozhat, ezért előbb nézd meg a számla befizetéseit:
```ts
import { isSzamlazzError, type PaymentEntry } from 'kassza'
async function befizetesRogzitese(szamlaszam: string, befizetes: PaymentEntry & { date: string }) {
try {
return await kassza.invoices.registerPayment({ invoiceNumber: szamlaszam, payments: [befizetes] })
} catch (error) {
if (!isSzamlazzError(error) || (error.category !== 'network' && error.category !== 'timeout')) {
throw error
}
const { payments } = await kassza.invoices.get(szamlaszam)
const rogzult = payments.some(
(meglevo) => meglevo.date === befizetes.date && meglevo.amount === befizetes.amount,
)
if (rogzult) return undefined
throw error
}
}
```
Ha a számla összes befizetését a saját adatbázisodban vezeted, egyszerűbb a teljes listát `additive: false` értékkel küldeni. Ez a hívás ismételhető, és a kassza hálózati hiba után magától újrapróbálja.
# Minta
URL: https://www.kasszajs.hu/docs/befizetes-rogzitese/minta
> Számla kiállítása, egy és több befizetés rögzítése, majd a befizetések törlése, a ténylegesen elküldött xmlszamlakifiz XML-lel.
A példa kiállít egy 127 000 bruttó végösszegű számlát, és három lépésben kezeli a befizetéseit:
1. rögzít egy 50 000 összegű első részletet átutalással,
2. egy kérésben hozzáad két további befizetést (20 000 készpénzben és 57 000 bankkártyával), így a befizetések összege eléri a végösszeget,
3. a `clearPayments()` hívással törli a számla összes befizetését.
Minden lépés után a válaszban ott a hátralék. Az **Elküldött XML** fülön az összes kérést megtalálod, köztük a három `xmlszamlakifiz` dokumentumot. A törlő kérésben nincs `kifizetes` elem, és az `additiv` értéke `false`.
## XSD séma [#xsd-séma]
A kassza az XML-t a Számlázz.hu `xmlszamlakifiz.xsd` sémája szerint, kötött elemsorrendben építi fel, és a tesztjei a generált kéréseket ezzel a sémával ellenőrzik:
```text
https://www.szamlazz.hu/szamla/docs/xsds/agentkifiz/xmlszamlakifiz.xsd
```
Az `aggregator` elembe a kassza a saját, a Számlázz.hu-tól kapott aggregátor kódját írja, így a Számlázz.hu látja, hogy a kérés a kasszán keresztül érkezett.
# IPN fizetési értesítés
URL: https://www.kasszajs.hu/docs/befizetes-rogzitese/ipn
> A Számlázz.hu kifizetési értesítésének (IPN) fogadása a kassza/ipn modullal, a beállítással, az újraküldés szabályaival, idempotens feldolgozással és a küldő IP-címek ellenőrzésével.
Az IPN (Instant Payment Notification) a Számlázz.hu webhookja. Ha egy bizonylat (számla vagy díjbekérő) kifizetett összege megváltozik, például részleges vagy teljes befizetés után, a Számlázz.hu HTTP POST kérést küld a megadott címre. A kérés törzse `application/x-www-form-urlencoded` formátumú, és a számla számát, a végösszeget és a kifizetett összeget tartalmazza.
Értesítés csak akkor készül, ha a változás a számla kiállítójának oldalán történik, és a fiókban be van állítva az IPN cím. A `kassza/ipn` modul beolvassa és típusos objektummá alakítja az értesítést, és segít ellenőrizni, hogy a kérés tényleg a Számlázz.hu-tól jött-e.
## Beállítás [#beállítás]
1. A Számlázz.hu fiókban nyisd meg a **Fiók beállítások → Számlázás alapadatok** oldalt, és az oldal alján lévő mezőbe írd be a végpontod címét, például `https://webshop.example.hu/api/szamlazz/ipn`.
2. A végpont a sikeres feldolgozás után HTTP 200 státusszal válaszoljon. A válasz törzse nem számít, csak a státuszkód.
3. Ha a végpontot IP-cím alapján korlátozod, engedélyezd a [küldő IP-címeket](#küldő-ip-címek).
A kifizetés dátumát (`szlahu_kifizdat`) a Számlázz.hu alapból nem küldi. Ha szükséged van rá,
az ügyfélszolgálaton kérheted a bekapcsolását. Addig a `paymentDate` mező `undefined`.
## Next.js route [#nextjs-route]
```ts title="app/api/szamlazz/ipn/route.ts"
import { ipnOkResponse, isSzamlazzIp, readIpnNotification } from 'kassza/ipn'
import { db } from '@/lib/db'
import { rendelesVisszaigazolas } from '@/lib/rendeles'
export async function POST(request: Request) {
if (!isSzamlazzIp(request.headers.get('x-forwarded-for') ?? '')) {
return new Response('Tiltott', { status: 403 })
}
const ipn = await readIpnNotification(request)
if (ipn.isFullyPaid) {
const { count } = await db.rendeles.updateMany({
where: { szamlaszam: ipn.invoiceNumber, fizetve: false },
data: { fizetve: true, fizetettOsszeg: ipn.paidAmount },
})
if (count > 0) await rendelesVisszaigazolas(ipn.invoiceNumber)
}
return ipnOkResponse()
}
```
A route csak akkor jelöli fizetettnek a rendelést, és csak akkor küld visszaigazolást, ha a rendelés még nem volt fizetett. Így egy ismételten beérkező értesítés nem küld második e-mailt. Ha a feldolgozás hibát dob, a Next.js 500-as státusszal válaszol, és a Számlázz.hu később újraküldi az értesítést.
Az `isSzamlazzIp()` az `x-forwarded-for` fejléc jobb szélső címét vizsgálja, azt, amelyet a hozzád legközelebbi proxy látott. Vercelen ez a kliens címe. Több proxy mögött add meg a `trustedProxies` opciót, a részleteket [lent](#az-x-forwarded-for-fejléc) olvashatod.
## Más keretrendszerben [#más-keretrendszerben]
A `readIpnNotification()` szabványos `Request` objektumot vár, így Hono, Cloudflare Workers, Bun és Deno alatt is közvetlenül használható:
```ts
import { Hono } from 'hono'
import { ipnOkResponse, isSzamlazzIp, readIpnNotification } from 'kassza/ipn'
const app = new Hono()
app.post('/szamlazz/ipn', async (c) => {
if (!isSzamlazzIp(c.req.header('cf-connecting-ip') ?? '')) return c.text('Tiltott', 403)
const ipn = await readIpnNotification(c.req.raw)
if (ipn.isFullyPaid) await rendelesFizetve(ipn)
return ipnOkResponse()
})
```
Ha a törzset már egy middleware beolvasta, a `parseIpnNotification()` az objektumból dolgozik:
```ts
import express from 'express'
import { isSzamlazzIp, parseIpnNotification } from 'kassza/ipn'
const app = express()
app.set('trust proxy', 1)
app.post('/szamlazz/ipn', express.urlencoded({ extended: false }), async (req, res) => {
if (!isSzamlazzIp(req.ip ?? '')) {
res.status(403).send('Tiltott')
return
}
const ipn = parseIpnNotification(req.body)
if (ipn.isFullyPaid) await rendelesFizetve(ipn)
res.status(200).send('OK')
})
```
## A kassza/ipn exportjai [#a-kasszaipn-exportjai]
| Export | Leírás |
| --- | --- |
| `readIpnNotification(request: Request)` | Beolvassa a kérést, és `Promise`-t ad. `multipart/form-data` törzset is kezel, üres törzsnél az URL query paramétereit olvassa. A `MAX_IPN_BODY_BYTES` (64 KiB) fölötti törzset `validation` hibával elutasítja. |
| `parseIpnNotification(input)` | Ugyanez már beolvasott adatból: szövegből, `URLSearchParams`-ból, `FormData`-ból vagy `Record` objektumból. |
| `ipnOkResponse()` | HTTP 200 `Response`, `OK` törzzsel. |
| `isSzamlazzIp(ip: string \| null \| undefined, options?)` | `true`, ha a cím a Számlázz.hu egyik küldő címe. Az `options.trustedProxies` a jobbról átugrandó megbízható proxyk száma (alapérték: `0`), az `options.allowedIps` a küldő címek egyedi listája. |
| `SZAMLAZZ_OUTBOUND_IPS` | A küldő IP-címek listája. |
| `IPN_FIELDS` | Az `IpnNotification` mezőneveit a Számlázz.hu paraméterneveire képezi. |
| `parseIpnAmount(value: string)` | Egy IPN összeget alakít számmá, értelmezhetetlen szövegnél `undefined`. |
## IpnNotification [#ipnnotification]
| Mező | Típus | IPN paraméter | Leírás |
| --- | --- | --- | --- |
| `invoiceNumber` | `string` | `szlahu_szamlaszam` | **Kötelező.** A számla száma. |
| `proformaNumber` | `string \| undefined` | `szlahu_dijbekero_szama` | A díjbekérő száma, ha a számla díjbekérőből készült. |
| `orderNumber` | `string \| undefined` | `szlahu_rendelesszam` | A rendelésszám, ha szerepelt a számlán. |
| `grossTotal` | `number` | `szlahu_bruttovegosszeg` | **Kötelező.** A számla bruttó végösszege. |
| `paidAmount` | `number` | `szlahu_kifizetettbrutto` | **Kötelező.** A kifizetett bruttó összeg. |
| `paymentMethod` | `string \| undefined` | `szlahu_fizetesmod` | A fizetés módja, például `'átutalás'` vagy `'bankkártya'`. |
| `paymentDate` | `string \| undefined` | `szlahu_kifizdat` | A kifizetés dátuma `'YYYY-MM-DD'` alakban, ha be van kapcsolva. |
| `isFullyPaid` | `boolean` | számolt | `true`, ha a kifizetett összeg eléri a végösszeget. |
| `raw` | `Readonly>` | minden paraméter | Az összes kapott paraméter szövegként, a fel nem dolgozottak is. |
Az üres opcionális mező `undefined` lesz. Ha egy kötelező mező hiányzik, vagy egy összeg nem értelmezhető, a kassza `validation` kategóriájú `SzamlazzError`-t dob. Az összegeknél a szóközös ezres tagolást (`'19 470'`), a tizedesvesszőt (`'12 700,50'`) és a tizedespontot (`'12700.5'`) is kezeli.
Az `isFullyPaid` a két összeg abszolút értékét hasonlítja össze, 0,005-ös tűréssel:
```ts
const isFullyPaid = Math.abs(paidAmount) + 0.005 >= Math.abs(grossTotal)
```
Így a túlfizetés is teljes kifizetésnek számít, és negatív végösszegnél, például helyesbítő számlánál is helyes az eredmény. Részfizetésnél `false`, a hátralék ilyenkor `grossTotal - paidAmount`.
## Kipróbálás [#kipróbálás]
A példa egy értesítést állít össze `Request` objektumként, ellenőrzi a küldő címét, beolvassa az értesítést, és kiírja a kapott `IpnNotification` objektumot és a 200-as választ.
## Újraküldés és sorrend [#újraküldés-és-sorrend]
A Számlázz.hu nem azonnal küldi ki az értesítéseket, hanem sorba teszi őket:
- Ha nem 200-as válasz jön, vagy a kérés időtúllépéssel ér véget, 3 percenként újrapróbálja, legfeljebb 10-szer. Utána az értesítést eldobja.
- Nagy forgalomnál az értesítések felgyűlhetnek a küldési sorban, és csak később érkeznek meg.
- Számlánként egyszerre csak egy értesítés vár kiküldésre. Ha közben újabb változás történik, csak a legfrissebb megy ki, a korábbiak törlődnek.
Ebből három szabály következik:
1. **Ne add össze az értesítéseket.** Egy részfizetésről lehet, hogy soha nem kapsz külön értesítést. Mindig a legutóbbi `paidAmount` és `grossTotal` alapján dönts, vagy az `isFullyPaid` mezőt használd.
2. **Válaszolj gyorsan.** A lassú munkát (e-mail, raktári rendelés) tedd háttérfeladatba, és azonnal küldd a 200-as választ. Egy lassú válasz után ugyanaz az értesítés újra megérkezhet.
3. **Legyen tartalék.** Ha a végpontod 10 próbálkozásnál tovább nem volt elérhető, az értesítés elveszett. Ilyenkor a nyitott rendelések számláit kérdezd le a [Számla adatainak lekérése](/docs/szamla-adatai) művelettel.
## Idempotens feldolgozás [#idempotens-feldolgozás]
Ugyanaz az értesítés többször is megérkezhet, ezért a feldolgozásnak ismételve is ugyanazt az eredményt kell adnia. A gyakorlatban:
- Az állapotot feltételesen frissítsd (a rendelés csak akkor legyen fizetett, ha még nem az), és a mellékhatásokat (visszaigazoló e-mail, szállítás indítása) csak akkor indítsd, ha a frissítés tényleg változtatott valamit, ahogy a fenti route teszi.
- A rendelést a `invoiceNumber` vagy az `orderNumber` alapján keresd, ne az értesítés beérkezési sorrendje alapján.
- Ha a számlára a saját kódod is rögzít befizetést a `registerPayment()` metódussal, az is a kiállító oldali változás, ezért értesítést válthat ki. Az IPN kezelőből ne rögzíts újra befizetést ugyanarra a számlára.
## Az IPN jelzés, nem bizonyíték [#az-ipn-jelzés-nem-bizonyíték]
Az IPN-t érdemes jelzésként kezelni: a tényleges állapotot (kifizetett összeg, hátralék) a [Számla adatainak lekérése](/docs/szamla-adatai) művelettel kérdezd le, mielőtt fontos döntést hozol. Így ha egy értesítés elveszik, vagy a forrás-IP ellenőrzése egy új Számlázz.hu címet tévesen elutasít, a rendelés állapota a következő lekérdezéskor akkor is helyreáll.
## Küldő IP-címek [#küldő-ip-címek]
A Számlázz.hu az értesítéseket 2025. augusztus 1. óta az alábbi címekről küldi. A korábbi címeket (`18.153.1.171`, `3.73.114.72`, `52.59.28.5`) már nem használja, ha még szerepelnek a tűzfalszabályaid között, cseréld le őket.
Ha a lista elavul (a Számlázz.hu új kimenő címet kap), az `isSzamlazzIp()` minden valódi értesítést elutasítana, csendben. A `checkSzamlazzIp()` ugyanezt vizsgálja, de megmondja az okot (`missing_header`, `no_client_entry`, `not_allowed`) és a vizsgált címet, így az elutasítás naplózható:
```ts
import { checkSzamlazzIp } from 'kassza/ipn'
const forras = checkSzamlazzIp(request.headers.get('x-forwarded-for'))
if (!forras.allowed) {
console.warn('IPN elutasítva', forras.reason, forras.client)
return new Response('Forbidden', { status: 403 })
}
```
Új címet az `allowedIps` opcióval adhatsz meg, amíg a kassza listája frissül. A kassza változásfigyelője hetente összeveti a listát a Számlázz.hu dokumentációjával.
Az `isSzamlazzIp()` a vizsgálat előtt egységes alakra hozza a címet: vesszővel elválasztott listából a jobb szélső elemet veszi (a `trustedProxies` számú megbízható proxy címét átugorva), levágja a szóközöket és a portot, és kezeli a `[...]:port` alakot és az IPv4-et IPv6-ba ágyazó `::ffff:` előtagot.
### Az x-forwarded-for fejléc [#az-x-forwarded-for-fejléc]
Az `x-forwarded-for` fejlécet bárki beállíthatja a kérésében, a proxyk pedig a fejléc **végére**
fűzik a látott címet. Ezért az `isSzamlazzIp()` a jobb szélső elemet vizsgálja, a bal oldali
részt a kliens hamisíthatja. Ha egynél több proxy áll előtted, a `trustedProxies` opcióval add
meg, hány jobb szélső címet ugorjon át. Ha ezt nem teszed, a valódi Számlázz.hu értesítést is
elutasíthatja, de hamisat nem fogad el.
| Környezet | Honnan olvasd a kliens címét? |
| --- | --- |
| Vercel | `x-forwarded-for` vagy `x-real-ip`: a Vercel felülírja őket, az alapbeállítás megfelel. |
| Cloudflare (Workers vagy proxy) | `cf-connecting-ip`. Az `x-forwarded-for` jobb szélső eleme is a kliens címe, ha Cloudflare után nincs másik proxy. |
| Saját nginx | A `$proxy_add_x_forwarded_for` a kliens címét fűzi a fejléc végére, ezt olvassa az alapbeállítás. Ha nginx előtt is van proxy (például load balancer), add meg `{ trustedProxies: 1 }`-et. |
| Express | `app.set('trust proxy', ...)` beállítás után a `req.ip`, a proxyk számának megfelelő értékkel. |
```ts
isSzamlazzIp(request.headers.get('x-forwarded-for'), { trustedProxies: 1 })
```
A Számlázz.hu dokumentációja az értesítéshez nem ír le aláírást, így a küldő címén kívül nincs mivel ellenőrizned a hitelességét. Ha egy értesítés alapján nagy értékű műveletet indítasz (például kiszállítást), előtte kérdezd le a számlát a `kassza.invoices.get()` metódussal, és a `payments` lista alapján dönts.
A kimenő és bejövő forgalom többi szabályát a [Hálózat és biztonság](/docs/alapok/halozat-es-biztonsag) oldal foglalja össze.
# Bizonylat lekérése PDF-ben
URL: https://www.kasszajs.hu/docs/bizonylat-pdf
> Egy korábban kiállított bizonylat PDF-jének letöltése számlaszám, rendelésszám vagy külső azonosító alapján a kassza invoices.getPdf() metódusával.
Ha a kiállításkor nem mentetted el a PDF-et, vagy `downloadPdf: false` beállítással kérted a számlát, később bármikor letöltheted. A kasszában ezt az `invoices.getPdf()` végzi: megmondod, melyik bizonylat kell, és `Uint8Array`-ként visszakapod a PDF-et, a bizonylat számával és végösszegeivel együtt.
```ts
const { pdf, number } = await kassza.invoices.getPdf({ orderNumber: 'REND-1001' })
```
A bizonylat azonosítása számlaszámmal, rendelésszámmal vagy külső azonosítóval.
Az InvoicePdf mezői, letöltés Next.js route-ból, mentés tárhelyre és a hibák.
Számla PDF nélkül, majd a PDF lekérése rendelésszám alapján a sandboxban.
A `getPdf()` a PDF mellett csak néhány összesítő mezőt ad. Ha a tételekre, a vevőre vagy a
befizetésekre is szükséged van, a [Számla adatainak lekérése](/docs/szamla-adatai) műveletet
használd, ott az `includePdf: true` a PDF-et is kéri. Nyugta PDF-jét a
[Nyugta lekérdezése](/docs/nyugta-lekerdezes) adja `downloadPdf: true` beállítással.
# Kérés
URL: https://www.kasszajs.hu/docs/bizonylat-pdf/keres
> Az invoices.getPdf() bemenete, a bizonylat azonosítása számlaszámmal, rendelésszámmal vagy külső azonosítóval, a Számla Agent xmlszamlapdf XML elemeivel.
```ts
kassza.invoices.getPdf(reference: InvoiceReference, options?: { signal?: AbortSignal }): Promise
```
A kassza `xmlszamlapdf` XML-t küld az `action-szamla_agent_pdf` form mezőben. A bizonylatot egyetlen hivatkozással azonosítod. Ha szöveget adsz át, azt a kassza számlaszámnak veszi:
```ts
const peldany = await kassza.invoices.getPdf('WEB-2026-128')
```
## A bizonylat azonosítása [#a-bizonylat-azonosítása]
```ts
type InvoiceReference =
| string
| { readonly invoiceNumber: string }
| { readonly orderNumber: string }
| { readonly externalId: string }
```
| Hivatkozás | XML elem | Mikor használd? |
| --- | --- | --- |
| `'WEB-2026-128'` vagy `{ invoiceNumber }` | `szamlaszam` | Ha eltároltad a bizonylat számát. Ez mindig pontosan egy bizonylatot jelöl. |
| `{ orderNumber }` | `rendelesSzam` | Ha csak a saját rendelésazonosítód van meg. Több azonos rendelésszámú bizonylat közül a legutóbbit kapod. |
| `{ externalId }` | `szamlaKulsoAzon` | Ha a kiállításkor megadtad az `externalId` mezőt. Enélkül ezzel nem található meg a bizonylat. |
```ts
const szamlaszamAlapjan = await kassza.invoices.getPdf({ invoiceNumber: 'WEB-2026-128' })
const rendelesAlapjan = await kassza.invoices.getPdf({ orderNumber: 'REND-1001' })
const kulsoAzonAlapjan = await kassza.invoices.getPdf({ externalId: 'webshop-1001' })
```
A hivatkozáson kívül a kassza csak a hitelesítést (`szamlaagentkulcs`, vagy `felhasznalo` és `jelszo`) írja az XML-be. A `valaszVerzio` elem értéke mindig `2`, így a Számlázz.hu XML-ben válaszol, benne a base64 kódolású PDF-fel.
## Szabályok [#szabályok]
- Pontosan egy hivatkozást adj meg. Üres szövegnél, üres értéknél vagy egyszerre több kulcsnál (például `invoiceNumber` és `orderNumber` együtt) a kassza `validation` hibát dob, és kérést nem küld. Az értékek elejéről és végéről a szóközöket levágja.
- Rendelésszám alapján a legutóbb kiállított bizonylatot kapod. Ha egy rendeléshez díjbekérő és az abból készült számla is tartozik, a számlát kapod vissza. Egy korábbi bizonylathoz a számlaszámával hivatkozz.
- A lekérdezés nem hoz létre semmit, ezért a kassza hálózati hibánál, 5xx válasznál, időtúllépésnél és karbantartásnál magától újrapróbálja, alapból háromszor. Lásd [Amit a kassza újrapróbál](/docs/alapok/hibakezeles#amit-a-kassza-újrapróbál).
- A második paraméterben saját `AbortSignal`-t adhatsz, lásd [Időkorlát és megszakítás](/docs/alapok/kliens-beallitasa#időkorlát-és-megszakítás).
```ts
const idokorlattal = await kassza.invoices.getPdf('WEB-2026-128', {
signal: AbortSignal.timeout(5_000),
})
```
# Válasz
URL: https://www.kasszajs.hu/docs/bizonylat-pdf/valasz
> Az InvoicePdf mezői, amelyeket az invoices.getPdf() visszaad, a PDF letöltése Next.js route-ból, mentése tárhelyre, és a PDF lekérésnél előforduló hibák.
Sikeres lekérés után a `getPdf()` egy `InvoicePdf` objektumot ad vissza. A `pdf` mező mindig ki van töltve: ha a Számlázz.hu válaszában nincs PDF, a kassza hibát dob. A többi mező a válasz XML-jéből jön, és ha ott hiányzik, a kassza a `szlahu_*` fejlécekből pótolja.
## InvoicePdf [#invoicepdf]
| Mező | Típus | Forrás | Leírás |
| --- | --- | --- | --- |
| `pdf` | `Uint8Array` | `pdf` | A bizonylat PDF-je. |
| `number` | `string \| undefined` | `szamlaszam` | A bizonylat száma. Rendelésszám vagy külső azonosító alapján ebből tudod meg, melyik bizonylatot kaptad. |
| `netTotal` | `number \| undefined` | `szamlanetto` | Nettó végösszeg. |
| `grossTotal` | `number \| undefined` | `szamlabrutto` | Bruttó végösszeg. |
| `outstanding` | `number \| undefined` | `kintlevoseg` | Hátralék. |
| `buyerAccountUrl` | `string \| undefined` | `vevoifiokurl` | A vevői fiók linkje, ha a fiókban be van kapcsolva. |
```ts
import { writeFile } from 'node:fs/promises'
const peldany = await kassza.invoices.getPdf({ orderNumber: 'REND-1001' })
await writeFile(`szamlak/${peldany.number}.pdf`, peldany.pdf)
```
A kassza XML választ kér (`valaszVerzio` 2), és a `pdf` elem base64 tartalmát `Uint8Array`-re
alakítja. Ha a Számlázz.hu mégis nyers PDF fájlt küld, azt is elfogadja, ilyenkor a mezőket a
`szlahu_*` fejlécekből tölti ki.
## Letöltés Next.js route-ból [#letöltés-nextjs-route-ból]
Az alábbi route a bejelentkezett vevőnek letöltésként adja vissza a rendeléséhez tartozó számlát. Az `auth()` és a `db` a saját alkalmazásod moduljai, a számlaszámot a kiállításkor mentetted el a rendelés mellé.
A számlaszámok sorszámozottak, ezért könnyen kitalálhatók. Ha a route az URL-ből kapott
azonosítót ellenőrzés nélkül továbbadja a `getPdf()`-nek, bárki letöltheti más vevők számláit, a
nevükkel és a címükkel együtt. Mindig ellenőrizd, hogy a bizonylat a bejelentkezett
felhasználóhoz tartozik-e.
```ts title="app/rendelesek/[id]/szamla/route.ts"
import { isSzamlazzError } from 'kassza'
import { auth } from '@/lib/auth'
import { db } from '@/lib/db'
import { kassza } from '@/lib/kassza'
export async function GET(_request: Request, { params }: RouteContext<'/rendelesek/[id]/szamla'>) {
const session = await auth()
if (!session) return new Response('Bejelentkezés szükséges.', { status: 401 })
const { id } = await params
const rendeles = await db.rendeles.findUnique({ where: { id } })
if (!rendeles?.szamlaszam || rendeles.userId !== session.user.id) {
return new Response('Nincs ilyen számla.', { status: 404 })
}
try {
const { pdf } = await kassza.invoices.getPdf(rendeles.szamlaszam)
return new Response(new Uint8Array(pdf), {
headers: {
'Content-Type': 'application/pdf',
'Content-Disposition': `attachment; filename="${rendeles.szamlaszam}.pdf"`,
'Cache-Control': 'private, no-store',
},
})
} catch (error) {
if (isSzamlazzError(error) && error.isNotFound) {
return new Response('Nincs ilyen számla.', { status: 404 })
}
throw error
}
}
```
- Más vevő rendelésére is 404-et ad, így a válaszból nem derül ki, hogy a rendelés létezik.
- A `new Uint8Array(pdf)` csak a TypeScript miatt kell: a DOM típusok a `Response` törzsénél `ArrayBuffer` alapú tömböt várnak, a `pdf` típusa pedig ennél általánosabb.
- Ha a PDF a böngészőben nyíljon meg, az `attachment` helyett `inline` értéket adj.
- Minden letöltés egy Számla Agent hívás. Ha a vevők gyakran töltik le a számlát, mentsd el egyszer tárhelyre, és onnan szolgáld ki.
## Mentés tárhelyre [#mentés-tárhelyre]
Ha a számlát `downloadPdf: false` beállítással állítottad ki, vagy a PDF mentése a kiállításkor nem sikerült, egy háttérfeladat később is letöltheti és elmentheti. A `kassza/storage` modul S3-ba, Cloudflare R2-be és más tárhelyekre ment:
```ts
import { invoicePdfKey, s3FetchStorage, storePdf } from 'kassza/storage'
import { kassza } from '@/lib/kassza'
const tarhely = s3FetchStorage({
bucket: 'szamlak',
region: 'auto',
endpoint: process.env.R2_ENDPOINT,
accessKeyId: process.env.R2_ACCESS_KEY_ID ?? '',
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY ?? '',
})
export async function szamlaPdfMentese(szamlaszam: string, kelte: string) {
const { pdf } = await kassza.invoices.getPdf(szamlaszam)
const fajl = await storePdf(tarhely, invoicePdfKey({ number: szamlaszam, date: kelte }), pdf)
return fajl.key
}
```
- Az `invoicePdfKey()` a számlaszámból és a dátumból képez kulcsot, például `szamlak/2026/09/WEB-2026-128.pdf`. A `date` nélkül a mai budapesti dátumot használja. A `type` mezővel (`'storno'`, `'proforma'` stb.) a bizonylat külön mappába kerül.
- A `storePdf()` feltöltés előtt ellenőrzi, hogy a tartalom valóban PDF, és ha nem, `StorageError`-t dob. A visszaadott `StoredFile` a `key`, `size`, `contentType` és, ha a tárhely ad ilyet, az `url` mezőt tartalmazza.
- A többi tárhely (AWS SDK, R2 binding, Vercel Blob, UploadThing, Supabase, fájlrendszer, memória) ugyanígy használható, lásd [PDF mentése tárhelyre](/docs/kiegeszitok/pdf-tarhely).
## Hibák [#hibák]
| Kód | Kategória | Mikor fordul elő? |
| --- | --- | --- |
| – | `validation` | Üres vagy többértelmű hivatkozás, például egyszerre `invoiceNumber` és `orderNumber`. Kérés nem megy ki. |
| 1 | `maintenance` | Karbantartás a Számlázz.hu oldalán. A kassza magától újrapróbálja. |
| 3 | `auth` | Hibás Agent kulcs. |
| 7 | `not_found` | Nincs bizonylat ezzel a számlaszámmal, rendelésszámmal vagy külső azonosítóval. Az `error.isNotFound` is `true`. |
| – | `unexpected_response` | A válasz sikeres, de nincs benne PDF, vagy a PDF nem érvényes base64. |
| – | `network`, `timeout` | Hálózati hiba, 5xx válasz vagy időtúllépés, és a próbálkozások elfogytak. Később nyugodtan próbáld újra, a lekérdezés nem hoz létre semmit. |
Az összes kódot a [Hibakezelés, hibakódok](/docs/alapok/hibakezeles#hibakódok) oldal sorolja fel.
## A kulcs ellenőrzése is ezt használja [#a-kulcs-ellenőrzése-is-ezt-használja]
A `kassza.verifyCredentials()` egy biztosan nem létező számlaszám PDF-jét kéri le. Ha a Számlázz.hu 7-es hibát ad, a kulcs jó, csak a számla nincs meg, ezért az eredmény `true`. `auth` hibánál `false`, minden más hibát továbbdob.
```ts
const kulcsRendben = await kassza.verifyCredentials()
```
Lásd [A kulcs ellenőrzése](/docs/alapok/hitelesites#a-kulcs-ellenőrzése).
# Minta
URL: https://www.kasszajs.hu/docs/bizonylat-pdf/minta
> Számla kiállítása PDF nélkül, majd a PDF lekérése rendelésszám alapján, a ténylegesen elküldött xmlszamlapdf XML-lel.
A példa `downloadPdf: false` beállítással állít ki egy számlát, ezért a `create()` válaszában a `pdf` értéke `undefined`. Utána a rendelésszám alapján a `getPdf()` metódussal kéri le a PDF-et. Az **Elküldött XML** fülön mindkét kérést látod: a második az `xmlszamlapdf` dokumentum, benne a `rendelesSzam` és a `valaszVerzio` elemmel.
## XSD séma [#xsd-séma]
```text
https://www.szamlazz.hu/szamla/docs/xsds/agentpdf/xmlszamlapdf.xsd
```
A sémában a `valaszVerzio` kötelező, a `szamlaszam`, a `rendelesSzam` és a `szamlaKulsoAzon` elhagyható. A kassza ezek közül mindig pontosan egyet ír ki, a séma által előírt sorrendben.
# Számla adatainak lekérése
URL: https://www.kasszajs.hu/docs/szamla-adatai
> Egy kiállított számla teljes adattartalma típusos objektumként a kassza invoices.get() és invoices.find() metódusával.
A `kassza.invoices.get()` egy kiállított számla minden adatát visszaadja: az eladót, a vevőt, a tételeket, az összegeket, a rögzített befizetéseket, és ha kéred, a PDF-et is. A Számla Agent ezt XML-ben küldi, a kassza `InvoiceDetails` objektummá alakítja. A `find()` ugyanezt csinálja, de ha nincs ilyen számla, hiba helyett `null`-t ad.
```ts
const szamla = await kassza.invoices.get({ orderNumber: 'REND-1001' })
console.log(szamla.header.number, szamla.header.reversed, szamla.totals.grossAmount)
const meglevo = await kassza.invoices.find({ orderNumber: 'REND-1002' })
if (meglevo === null) console.log('Ehhez a rendeléshez még nincs számla.')
```
Lekérés számlaszám, rendelésszám vagy külső azonosító alapján, a get és a find különbsége.
Az InvoiceDetails teljes szerkezete a válasz XML elemeivel, és a lekérésnél előforduló hibák.
Számla kiállítása, az adatai lekérése, és a find egy nem létező rendelésre.
Beérkező számlát, vagy olyat, amelyet nem a Számlázz.hu-ban állítottak ki, ezzel a művelettel nem
tudsz lekérni. Ha csak a PDF kell, használd a [Bizonylat lekérése PDF-ben](/docs/bizonylat-pdf)
műveletet.
# Kérés
URL: https://www.kasszajs.hu/docs/szamla-adatai/keres
> Az invoices.get() és invoices.find() bemenete, a bizonylat azonosítása, a PDF kérése és a Számla Agent xmlszamlaxml XML elemei.
```ts
kassza.invoices.get(reference: InvoiceReference, query?: { includePdf?: boolean }, options?: { signal?: AbortSignal }): Promise
kassza.invoices.find(reference: InvoiceReference, query?: { includePdf?: boolean }, options?: { signal?: AbortSignal }): Promise
```
A kassza `xmlszamlaxml` XML-t küld az `action-szamla_agent_xml` form mezőben. A számlát háromféleképpen azonosíthatod:
```ts
const szamla = await kassza.invoices.get('WEB-2026-128')
const ugyanaz = await kassza.invoices.get({ invoiceNumber: 'WEB-2026-128' })
```
```ts
const szamla = await kassza.invoices.get({ orderNumber: 'REND-1001' })
```
```ts
const szamla = await kassza.invoices.get({ externalId: 'webshop-8f3a2c' })
```
## Mezők [#mezők]
| Mező | XML elem | Alapérték | Leírás |
| --- | --- | --- | --- |
| `reference` (szöveg) vagy `reference.invoiceNumber` | `szamlaszam` | – | A számla száma. |
| `reference.orderNumber` | `rendelesSzam` | – | A számla rendelésszáma. Ha több bizonylaton is szerepel, a legutóbbit kapod. |
| `reference.externalId` | `szamlaKulsoAzon` | – | Külső azonosító. Csak akkor működik, ha kiállításkor megadtad az `externalId` mezőt. |
| `query.includePdf` | `pdf` | nincs PDF | `true` esetén a válasz a számla PDF-jét is tartalmazza. |
| `options.signal` | – | – | `AbortSignal`, amellyel megszakíthatod a kérést. |
A hivatkozásban pontosan egy kulcs szerepeljen. Üres érték, hiányzó vagy egyszerre több kulcs esetén a kassza `validation` hibát dob, és kérés nem megy ki.
```ts
const szamla = await kassza.invoices.get({ orderNumber: 'REND-1001' }, { includePdf: true })
if (szamla.pdf) await writeFile(`szamlak/${szamla.header.number}.pdf`, szamla.pdf)
```
## get vagy find? [#get-vagy-find]
A `get()` akkor is hibát dob, ha a számla nem létezik: a Számlázz.hu 7-es kódot ad, amelyből `not_found` kategóriájú `SzamlazzError` lesz. A `find()` pontosan ezt az esetet fordítja `null`-ra, minden más hibát ugyanúgy továbbdob.
```ts
const szamla = await kassza.invoices.find({ orderNumber: 'REND-1001' })
if (szamla === null) await kassza.invoices.create({ orderNumber: 'REND-1001', buyer, items })
```
Ha a számlának biztosan léteznie kell, például egy mentett számlaszám alapján, a `get()` a jobb választás. Ha a hiánya is normális eset, használd a `find()`-ot.
## Idempotens számlázás [#idempotens-számlázás]
A `find()` a dupla számla elleni védekezés alapja. Számlakészítés előtt nézd meg, van-e már számla a rendelésszámhoz, és bizonytalan hiba után is előbb ezt ellenőrizd, mielőtt újra kiállítanád:
```ts
import { type CreateInvoiceInput, isSzamlazzError } from 'kassza'
const BIZONYTALAN = ['network', 'timeout', 'partial_success', 'duplicate']
async function szamlaz(orderNumber: string, adatok: CreateInvoiceInput) {
const meglevo = await kassza.invoices.find({ orderNumber })
if (meglevo) return meglevo.header.number
try {
const szamla = await kassza.invoices.create({ ...adatok, orderNumber })
return szamla.number
} catch (error) {
if (isSzamlazzError(error) && BIZONYTALAN.includes(error.category)) {
const letrejott = await kassza.invoices.find({ orderNumber })
if (letrejott) return letrejott.header.number
}
throw error
}
}
```
Sztornózás után a rendelésszám újra felhasználható, ezért a `find()` találata lehet egy
sztornózott számla is. Ha ez nálad előfordulhat, a találatnál nézd meg a `header.type` és a
`header.reversed` mezőt is.
A teljes mintát a [Hibakezelés](/docs/alapok/hibakezeles#bizonytalan-kimenet-számla-készült-vagy-nem) és a [Rendelésszám](/docs/szamla-letrehozas/beallitasok-es-szabalyok/rendelesszam#a-kassza-ajánlott-mintája) oldal részletezi.
## Szabályok [#szabályok]
- Csak a Számlázz.hu-ban kiállított kimenő számlák adatai kérhetők le.
- Rendelésszám alapján mindig a legutóbbi bizonylat jön vissza, ha több is tartozik hozzá.
- Külső azonosítóval csak az a számla kérhető le, amelynél kiállításkor megadtad.
- A lekérés semmit nem módosít, ezért a kassza `maintenance`, `network` és `timeout` hibánál **magától újrapróbálja**, alapból legfeljebb három próbálkozással. Lásd [Amit a kassza újrapróbál](/docs/alapok/hibakezeles#amit-a-kassza-újrapróbál).
# Válasz
URL: https://www.kasszajs.hu/docs/szamla-adatai/valasz
> Az InvoiceDetails teljes szerkezete, amelyet az invoices.get() visszaad, a Számlázz.hu szamla XML elemeivel, és a lekérésnél előforduló hibák.
Sikeres lekérésnél a Számlázz.hu egy `szamla` gyökérelemű XML-t küld (séma: `szamla.xsd`), amelyből a `get()` és a `find()` egy `InvoiceDetails` objektumot épít. Hiba esetén `xmlszamlavalasz` XML jön, abból `SzamlazzError` lesz.
A kassza a válaszon néhány egységesítést végez:
- az üres XML elemekből `undefined` lesz,
- a számokat `number`-ré alakítja, a tizedesvesszőt is kezeli,
- a `true`/`false` és az `1`/`0` értékekből `boolean` lesz,
- a hiányzó listákból (tételek, pénzügyi tételek, címkék, befizetések) üres tömb lesz.
```ts
const szamla = await kassza.invoices.get({ orderNumber: 'REND-1001' })
for (const tetel of szamla.items) console.log(tetel.name, tetel.quantity, tetel.grossAmount)
console.log(szamla.totals.grossAmount, szamla.payments.length)
```
Az `InvoiceDetails`, `InvoiceDetailsSeller`, `InvoiceDetailsHeader`, `InvoiceDetailsBuyer`,
`InvoiceDetailsItem`, `InvoiceDetailsTotals`, `InvoiceDetailsPayment` és `InvoiceDocumentType`
típust a `kassza` csomagból importálhatod. A többi altípust indexeléssel éred el, például
`InvoiceDetails['financialItems'][number]`.
## InvoiceDetails [#invoicedetails]
| Mező | Típus | XML elem | Leírás |
| --- | --- | --- | --- |
| `seller` | `InvoiceDetailsSeller` | `szallito` | Az eladó, vagyis a te céged adatai. |
| `header` | `InvoiceDetailsHeader` | `alap` | Számlaszám, típus, dátumok, fizetési mód, pénznem, állapot. |
| `buyer` | `InvoiceDetailsBuyer` | `vevo` | A vevő adatai. |
| `items` | `InvoiceDetailsItem[]` | `tetelek/tetel` | A számla tételei. |
| `financialItems` | `InvoiceDetailsFinancialItem[]` | `qutetek/qutet` | Pénzügyi tételek. |
| `labels` | `string[]` | `cimkek/cimke` | A számla címkéi. Az üres címkéket a kassza kihagyja. |
| `totals` | `InvoiceDetailsTotals` | `osszegek` | Végösszegek, áfakulcsonként is. |
| `payments` | `InvoiceDetailsPayment[]` | `kifizetesek/kifizetes` | A számlán rögzített befizetések. |
| `pdf` | `Uint8Array \| undefined` | `pdf` | A számla PDF-je Base64-ből dekódolva. Csak `includePdf: true` esetén van. |
## Eladó (seller) [#eladó-seller]
| Mező | Típus | XML elem | Leírás |
| --- | --- | --- | --- |
| `id` | `number \| undefined` | `id` | Az eladó azonosítója a Számlázz.hu-ban. |
| `name` | `string` | `nev` | Az eladó neve. |
| `address` | `InvoiceDetailsAddress \| undefined` | `cim` | Az eladó címe, lásd [Cím](#cím). |
| `postalAddress` | `InvoiceDetailsAddress \| undefined` | `postacim` | Postázási cím. |
| `taxNumber` | `string \| undefined` | `adoszam` | Adószám. |
| `groupTaxNumber` | `string \| undefined` | `csoportazonosito` | Csoportos adószám (csoportazonosító). |
| `euTaxNumber` | `string \| undefined` | `adoszameu` | Közösségi adószám. |
| `bank.name` | `string \| undefined` | `bank/nev` | A bank neve. |
| `bank.accountNumber` | `string \| undefined` | `bank/bankszamla` | Bankszámlaszám. |
A `bank` maga is `undefined`, ha a válaszban nincs `bank` elem.
## Cím [#cím]
Az `address` és a `postalAddress` szerkezete az eladónál és a vevőnél is ugyanaz. Minden mezője `string | undefined`.
| Mező | XML elem | Leírás |
| --- | --- | --- |
| `name` | `nev` | Név, jellemzően a postázási címnél. |
| `country` | `orszag` | Ország. |
| `zip` | `irsz` | Irányítószám. |
| `city` | `telepules` | Település. |
| `address` | `cim` | Utca, házszám. |
## Fejléc (header) [#fejléc-header]
| Mező | Típus | XML elem | Leírás |
| --- | --- | --- | --- |
| `id` | `number \| undefined` | `id` | A bizonylat azonosítója a Számlázz.hu-ban. |
| `number` | `string` | `szamlaszam` | A bizonylat száma. |
| `economicEventId` | `number \| undefined` | `gazdEsemAzon` | Gazdasági esemény azonosító. |
| `sourceSystem` | `number \| undefined` | `forras` | A bizonylat forrásának kódja. |
| `registrationNumber` | `string \| undefined` | `iktatoszam` | Iktatószám. |
| `type` | `InvoiceDocumentType` | `tipus` | A bizonylat típusa, lásd [Bizonylattípus](#bizonylattípus). |
| `typeCode` | `string` | `tipus` | A nyers típuskód, például `'SZ'`. |
| `eInvoice` | `boolean` | `eszamla` | `true`, ha az `eszamla` kód `2` vagy `3`. |
| `appearanceCode` | `number \| undefined` | `eszamla` | A nyers `eszamla` kód. |
| `referencedInvoiceNumber` | `string \| undefined` | `hivszamlaszam` | A hivatkozott számla száma. |
| `referencedProformaNumber` | `string \| undefined` | `hivdijbekszam` | A hivatkozott díjbekérő száma. |
| `issueDate` | `string` | `kelt` | A kelte, `'YYYY-MM-DD'` formában. |
| `fulfillmentDate` | `string \| undefined` | `telj` | A teljesítés dátuma. |
| `dueDate` | `string \| undefined` | `fizh` | A fizetési határidő. |
| `paymentMethod` | `string \| undefined` | `fizmod` | Fizetési mód. |
| `unifiedPaymentMethod` | `string \| undefined` | `fizmodunified` | Egységesített fizetési mód. |
| `cash` | `boolean \| undefined` | `keszpenz` | Készpénzes fizetés jelölése. |
| `orderNumber` | `string \| undefined` | `rendelesszam` | Rendelésszám. |
| `language` | `string \| undefined` | `nyelv` | A számla nyelve, például `'hu'`. |
| `currency` | `string \| undefined` | `devizanem` | Pénznem, például `'HUF'`. |
| `exchangeBank` | `string \| undefined` | `devizabank` | Az árfolyamot jegyző bank. |
| `exchangeRate` | `number \| undefined` | `devizaarf` | Árfolyam. |
| `comment` | `string \| undefined` | `megjegyzes` | A számla megjegyzése. |
| `vatType` | `string \| undefined` | `afatipus` | Áfatípus jelölése. |
| `cashAccounting` | `boolean \| undefined` | `penzforg` | Pénzforgalmi elszámolás. |
| `kata` | `boolean \| undefined` | `kata` | KATA jelölés. |
| `kataLedger` | `boolean \| undefined` | `katafokonyv` | KATA főkönyvi jelölés. |
| `email` | `string \| undefined` | `email` | A számlához tartozó e-mail cím. |
| `test` | `boolean \| undefined` | `teszt` | `true` teszt cégnél, `false` éles cégnél. |
| `reversed` | `boolean \| undefined` | `sztornozott` | `true`, ha a számlát már sztornózták. |
### Bizonylattípus [#bizonylattípus]
| `type` | `typeCode` | Bizonylat |
| --- | --- | --- |
| `'invoice'` | `SZ` | Számla |
| `'proforma'` | `D` | Díjbekérő |
| `'advance'` | `ES` | Előlegszámla |
| `'final'` | `VS` | Végszámla |
| `'corrective'` | `HS` | Helyesbítő számla |
| `'reversal'` | `SS` | Sztornó számla |
| `'deliveryNote'` | `SL` | Szállítólevél |
| `'unknown'` | bármi más | Ismeretlen kód. A nyers értéket a `typeCode` tartalmazza. |
```ts
const szamla = await kassza.invoices.get('WEB-2026-128')
const ervenyes = szamla.header.type === 'invoice' && szamla.header.reversed !== true
```
## Vevő (buyer) [#vevő-buyer]
| Mező | Típus | XML elem | Leírás |
| --- | --- | --- | --- |
| `id` | `number \| undefined` | `id` | A vevő azonosítója a Számlázz.hu-ban. |
| `name` | `string` | `nev` | A vevő neve. |
| `identifier` | `string \| undefined` | `azonosito` | Partner azonosító a fiók partnertörzsében. |
| `address` | `InvoiceDetailsAddress \| undefined` | `cim` | Számlázási cím, lásd [Cím](#cím). |
| `postalAddress` | `InvoiceDetailsAddress \| undefined` | `postacim` | Postázási cím. |
| `email` | `string \| undefined` | `email` | E-mail cím. |
| `taxNumber` | `string \| undefined` | `adoszam` | Magyar adószám. |
| `groupTaxNumber` | `string \| undefined` | `csoportazonosito` | Csoportos adószám (csoportazonosító). |
| `euTaxNumber` | `string \| undefined` | `adoszameu` | Közösségi adószám. |
| `location` | `number \| undefined` | `lokacio` | A vevő lokáció kódja. |
| `privatePerson` | `boolean \| undefined` | `privatePersonIndicator` | Magánszemély jelölése. |
| `ledger` | `InvoiceDetailsBuyerLedger \| undefined` | `fokonyv` | Könyvelési adatok, lásd lent. |
### Vevő könyvelési adatai (buyer.ledger) [#vevő-könyvelési-adatai-buyerledger]
| Mező | Típus | XML elem | Leírás |
| --- | --- | --- | --- |
| `ledgerAccount` | `string \| undefined` | `vevo` | Vevő főkönyvi száma. |
| `buyerId` | `string \| undefined` | `vevoazon` | Vevő azonosító a könyvelésben. |
| `bookingDate` | `string \| undefined` | `datum` | Könyvelés dátuma. |
| `continuousFulfillment` | `boolean \| undefined` | `folyamatostelj` | Folyamatos teljesítés. |
| `settlementPeriodStart` | `string \| undefined` | `elszDatTol` | Elszámolási időszak kezdete. |
| `settlementPeriodEnd` | `string \| undefined` | `elszDatIg` | Elszámolási időszak vége. |
## Tételek (items) [#tételek-items]
| Mező | Típus | XML elem | Leírás |
| --- | --- | --- | --- |
| `name` | `string` | `nev` | A tétel megnevezése. |
| `identifier` | `string \| undefined` | `azonosito` | Cikkszám vagy termékazonosító. |
| `quantity` | `number` | `mennyiseg` | Mennyiség. |
| `unit` | `string \| undefined` | `mennyisegiegyseg` | Mennyiségi egység. |
| `netUnitPrice` | `number` | `nettoegysegar` | Nettó egységár. |
| `vat` | `number` | `afakulcs` | Áfakulcs számként, például `27`. |
| `vatCode` | `string \| undefined` | `afatipus` | Szöveges áfatípus. |
| `netAmount` | `number` | `netto` | Nettó érték. |
| `marginVatBase` | `number \| undefined` | `arresafaalap` | Különbözeti adózásnál az árrés áfa alapja. |
| `vatAmount` | `number` | `afa` | Áfa érték. |
| `grossAmount` | `number` | `brutto` | Bruttó érték. |
| `comment` | `string \| undefined` | `megjegyzes` | A tétel megjegyzése. |
| `position` | `number \| undefined` | `sztetordering` | A tétel sorrendje a számlán. |
| `ledger` | `InvoiceDetailsItemLedger \| undefined` | `fokonyv` | Könyvelési adatok, lásd lent. |
### Tétel könyvelési adatai (items[].ledger) [#tétel-könyvelési-adatai-itemsledger]
| Mező | Típus | XML elem | Leírás |
| --- | --- | --- | --- |
| `revenueLedgerAccount` | `string \| undefined` | `arbevetel` | Árbevétel főkönyvi szám. |
| `vatLedgerAccount` | `string \| undefined` | `afa` | Áfa főkönyvi szám. |
| `economicEvent` | `string \| undefined` | `gazdasagiesemeny` | Gazdasági esemény. |
| `vatEconomicEvent` | `string \| undefined` | `gazdasagiesemenyafa` | Áfa gazdasági esemény. |
| `settlementPeriodStart` | `string \| undefined` | `elszdattol` | Elszámolási időszak kezdete. |
| `settlementPeriodEnd` | `string \| undefined` | `elszdatig` | Elszámolási időszak vége. |
## Pénzügyi tételek (financialItems) [#pénzügyi-tételek-financialitems]
| Mező | Típus | XML elem | Leírás |
| --- | --- | --- | --- |
| `name` | `string` | `nev` | A tétel megnevezése. |
| `vat` | `number` | `afakulcs` | Áfakulcs számként. |
| `vatCode` | `string \| undefined` | `afatipus` | Szöveges áfatípus. |
| `netAmount` | `number` | `netto` | Nettó érték. |
| `vatAmount` | `number` | `afa` | Áfa érték. |
| `grossAmount` | `number` | `brutto` | Bruttó érték. |
| `settlementPeriodStart` | `string \| undefined` | `elszdattol` | Elszámolási időszak kezdete. |
| `settlementPeriodEnd` | `string \| undefined` | `elszdatig` | Elszámolási időszak vége. |
| `vatDeduction` | `number \| undefined` | `afalevon` | Áfalevonás jelölése. |
| `labels` | `string[]` | `cimkek/cimke` | A tétel címkéi. |
## Összegek (totals) [#összegek-totals]
| Mező | Típus | XML elem | Leírás |
| --- | --- | --- | --- |
| `netAmount` | `number` | `totalossz/netto` | Nettó végösszeg. |
| `vatAmount` | `number` | `totalossz/afa` | Áfa végösszeg. |
| `grossAmount` | `number` | `totalossz/brutto` | Bruttó végösszeg. |
| `byVat` | `InvoiceDetailsVatTotal[]` | `afakulcsossz` | Összesítés áfakulcsonként, lásd lent. |
### Áfakulcsonkénti összesítés (totals.byVat) [#áfakulcsonkénti-összesítés-totalsbyvat]
| Mező | Típus | XML elem | Leírás |
| --- | --- | --- | --- |
| `vat` | `number` | `afakulcs` | Áfakulcs számként. |
| `vatCode` | `string \| undefined` | `afatipus` | Szöveges áfatípus. |
| `netAmount` | `number` | `netto` | Nettó összeg ezzel a kulccsal. |
| `vatAmount` | `number` | `afa` | Áfa összeg ezzel a kulccsal. |
| `grossAmount` | `number` | `brutto` | Bruttó összeg ezzel a kulccsal. |
## Befizetések (payments) [#befizetések-payments]
| Mező | Típus | XML elem | Leírás |
| --- | --- | --- | --- |
| `date` | `string` | `datum` | A befizetés dátuma. |
| `method` | `string` | `jogcim` | A befizetés módja, ugyanaz, mint a [Befizetés rögzítése](/docs/befizetes-rogzitese) `method` mezője. |
| `amount` | `number` | `osszeg` | A befizetett összeg. |
| `comment` | `string \| undefined` | `megjegyzes` | Megjegyzés. |
| `bankAccountNumber` | `string \| undefined` | `bankszamlaszam` | Bankszámlaszám. |
| `bankTransactionId` | `number \| undefined` | `banktranzid` | Banki tranzakció azonosító. |
| `exchangeRate` | `number \| undefined` | `devizaarf` | Árfolyam. |
## Kötelező elemek [#kötelező-elemek]
A táblázatokban a nem `undefined` típusú mezőkre számíthatsz. A kassza ezeket az elemeket kötelezőnek veszi:
- `szallito/nev`, `alap/szamlaszam`, `alap/tipus`, `alap/kelt`, `vevo/nev`,
- `osszegek/totalossz` és benne a `netto`, `afa`, `brutto`,
- minden tételnél, pénzügyi tételnél és áfakulcsonkénti összesítésnél a `nev` (ahol van), az `afakulcs`, a `netto`, az `afa` és a `brutto`, a tételeknél a `mennyiseg` és a `nettoegysegar` is,
- minden befizetésnél a `datum`, a `jogcim` és az `osszeg`.
Ha ezek közül valamelyik hiányzik, vagy a gyökérelem nem `szamla`, a kassza nem ad vissza félkész objektumot, hanem `unexpected_response` hibát dob, amelynek az üzenete megnevezi a hiányzó elemet.
## Hibák [#hibák]
| Kód | Kategória | Mikor fordul elő? |
| --- | --- | --- |
| – | `validation` | Üres hivatkozás, vagy nem pontosan egy kulcs a hivatkozásban. Kérés nem megy ki. |
| 1 | `maintenance` | Karbantartás. A kassza magától újrapróbálja. |
| 3 | `auth` | Hibás Agent kulcs. |
| 7 | `not_found` | Nincs ilyen számlaszám, rendelésszám vagy külső azonosító. A `find()` ilyenkor `null`-t ad. |
| – | `network`, `timeout` | Hálózati hiba vagy időtúllépés. A kassza magától újrapróbálja, és csak az utolsó sikertelen próbálkozás után dob. |
| – | `unexpected_response` | A válasz nem `szamla` XML, hiányzik belőle egy kötelező elem, vagy a PDF nem érvényes Base64. |
```ts
import { isSzamlazzError } from 'kassza'
try {
const szamla = await kassza.invoices.get(szamlaszam)
console.log(szamla.header.reversed)
} catch (error) {
if (isSzamlazzError(error) && error.isNotFound) console.warn(`Nincs ilyen számla: ${szamlaszam}`)
else throw error
}
```
Az összes kódot a [Hibakezelés, hibakódok](/docs/alapok/hibakezeles#hibakódok) oldal sorolja fel.
# Minta
URL: https://www.kasszajs.hu/docs/szamla-adatai/minta
> Számla kiállítása, az adatai lekérése rendelésszám alapján, és a find egy nem létező rendelésre, a ténylegesen elküldött xmlszamlaxml XML-lel.
A példa kiállít egy kártyával kifizetett számlát két tétellel, majd a rendelésszám alapján lekéri a fejlécét, az összegeit és a befizetéseit. Végül a `find()` egy nem létező rendelésszámra `null`-t ad hiba helyett. Az **Elküldött XML** fülön a számlakészítés mellett mindkét `xmlszamlaxml` kérést látod, a **Válasz** fülön pedig a `szamla` XML-t és a nem létező rendelésre kapott 7-es hibát.
## XSD séma [#xsd-séma]
A kérés XML sémája, a mezők sorrendje kötött:
```text
https://www.szamlazz.hu/szamla/docs/xsds/agentxml/xmlszamlaxml.xsd
```
A kassza a `szamlaszam`, `rendelesSzam`, `pdf` és `szamlaKulsoAzon` elemet ebben a sorrendben írja ki, és csak azt, amelyiket megadtad. A válasz szerkezetét a [Válasz](/docs/szamla-adatai/valasz) oldal írja le mezőről mezőre.
# Díjbekérő törlése
URL: https://www.kasszajs.hu/docs/dijbekero-torlese
> Ki nem fizetett díjbekérő törlése díjbekérőszám vagy rendelésszám alapján a kassza invoices.deleteProforma() metódusával.
A díjbekérő nem számla, ezért nem sztornózod, hanem törlöd. A kasszában ezt az `invoices.deleteProforma()` végzi: megadod a díjbekérő számát vagy a rendelésszámát, és sikeres törlés után a metódus nem ad vissza semmit.
```ts
await kassza.invoices.deleteProforma('D-2026-77')
```
## Törlés vagy számla? [#törlés-vagy-számla]
- **A vevő nem fizetett, és már nem is fog:** töröld a díjbekérőt.
- **A vevő kifizette:** ne töröld. Állíts ki róla számlát, és a `proformaNumber` mezőben hivatkozz rá, lásd [Bizonylattípusok](/docs/szamla-letrehozas/beallitasok-es-szabalyok/bizonylattipusok#díjbekérő).
- **Sztornózni akarod:** nem lehet. Díjbekérőre az `invoices.reverse()` `validation` hibát dob.
A három hivatkozási forma és az xmlszamladbkdel XML elemei.
A visszatérési érték, a 335-ös hiba, és mi a teendő hálózati hiba után.
Díjbekérő kiállítása, törlése és a második törlés hibája a sandboxban.
Ha rendelésszám alapján törölsz, és a fiókban több díjbekérő is ezzel a rendelésszámmal szerepel,
a Számlázz.hu mindegyiket törli. Egyetlen díjbekérőhöz a számát add meg.
# Kérés
URL: https://www.kasszajs.hu/docs/dijbekero-torlese/keres
> Az invoices.deleteProforma() bemenete, a díjbekérő három hivatkozási formája és a Számla Agent xmlszamladbkdel XML elemei.
```ts
kassza.invoices.deleteProforma(reference: ProformaReference, options?: { signal?: AbortSignal }): Promise
```
A kassza `xmlszamladbkdel` XML-t küld az `action-szamla_agent_dijbekero_torlese` form mezőben. A díjbekérőt a számával vagy a rendelésszámával azonosítod:
```ts
await kassza.invoices.deleteProforma('D-2026-77')
```
```ts
await kassza.invoices.deleteProforma({ proformaNumber: 'D-2026-77' })
```
```ts
await kassza.invoices.deleteProforma({ orderNumber: 'NEV-2026-077' })
```
## Mezők [#mezők]
| Hivatkozás | XML elem | Leírás |
| --- | --- | --- |
| `string` | `fejlec/szamlaszam` | A díjbekérő száma. |
| `{ proformaNumber }` | `fejlec/szamlaszam` | Ugyanez objektumként. |
| `{ orderNumber }` | `fejlec/rendelesszam` | A díjbekérő rendelésszáma. |
A `beallitasok` blokkba a kassza a hitelesítési adatokat írja: az Agent kulcsot (`szamlaagentkulcs`), vagy a felhasználónevet és a jelszót (`felhasznalo`, `jelszo`), lásd [Hitelesítés](/docs/alapok/hitelesites). Más beállítása ennek a kérésnek nincs.
## Szabályok [#szabályok]
- Pontosan egy azonosítót adj meg. Üres szövegre, üres objektumra, vagy ha a `proformaNumber` és az `orderNumber` is szerepel, a kassza `validation` hibát dob, és nem küld kérést.
- Az értékek elejéről és végéről a kassza levágja a szóközöket.
- Az XML elem itt kisbetűs `rendelesszam`, nem a számlakészítésnél használt `rendelesSzam`. A kassza ezt magától kezeli, csak a nyers XML összevetésénél számít.
- Rendelésszám alapján a Számlázz.hu a fiók összes ilyen rendelésszámú díjbekérőjét törli.
- A kassza a törlést **soha nem próbálja újra** magától, hálózati hiba vagy időtúllépés után sem. Hogy ilyenkor mit tegyél, a [Válasz](/docs/dijbekero-torlese/valasz#hálózati-hiba-után) oldal írja le.
# Válasz
URL: https://www.kasszajs.hu/docs/dijbekero-torlese/valasz
> Mit ad vissza az invoices.deleteProforma(), milyen hibák fordulnak elő díjbekérő törlésekor, és mi a teendő hálózati hiba után.
A `deleteProforma()` visszatérési értéke `Promise`: sikeres törlés után nincs visszaadott mező. Ha a promise hiba nélkül teljesül, a díjbekérő törlődött.
A Számlázz.hu `xmlszamladbkdelvalasz` XML-lel válaszol. A kassza csak akkor tekinti sikeresnek, ha a `sikeres` elem `true`, minden más esetben `SzamlazzError` hibát dob.
| XML elem | kassza | Leírás |
| --- | --- | --- |
| `sikeres` | – | `true` esetén a promise teljesül, különben hiba jön. |
| `hibakod` | `error.code` | A Számlázz.hu hibakódja. |
| `hibauzenet` | `error.message` | A Számlázz.hu hibaüzenete, a kód elé írva. |
## Hibák [#hibák]
| Kód | Kategória | Mikor fordul elő? |
| --- | --- | --- |
| – | `validation` | Üres azonosító, vagy a hivatkozásban nem pontosan egy mező van. A kassza nem küld kérést. |
| 3 | `auth` | Hibás Agent kulcs. |
| 335 | `not_found` | Nincs ilyen díjbekérő: nem is létezett, vagy már törölték. |
| – | `unexpected_response` | A válasz nem a várt XML, vagy hiányzik belőle a `sikeres` `true` érték. |
| – | `network`, `timeout` | Nem tudni, törlődött-e a díjbekérő. A kassza nem küldi újra. |
A 335-ös hibához a kassza `hint` mezőben magyar tippet is ad. A többi kódot a [Hibakezelés, hibakódok](/docs/alapok/hibakezeles#hibakódok) oldalon találod.
## Hálózati hiba után [#hálózati-hiba-után]
Hálózati hiba vagy időtúllépés után nem tudhatod, hogy a Számlázz.hu megkapta-e a kérést. Ilyenkor küldd el még egyszer a törlést: ha 335-ös hibát kapsz, a díjbekérő már nincs meg, vagyis az első kérés valószínűleg célba ért.
```ts
import { isSzamlazzError } from 'kassza'
const BIZONYTALAN = ['network', 'timeout']
async function dijbekeroTorlese(dijbekeroSzam: string) {
try {
await kassza.invoices.deleteProforma(dijbekeroSzam)
} catch (error) {
if (!isSzamlazzError(error) || !BIZONYTALAN.includes(error.category)) throw error
try {
await kassza.invoices.deleteProforma(dijbekeroSzam)
} catch (ismetelt) {
if (!isSzamlazzError(ismetelt) || ismetelt.code !== 335) throw ismetelt
}
}
}
```
Ha újraküldés helyett előbb ellenőriznél, kérdezd le a díjbekérőt a számával. Az `invoices.find()` `null`-t ad, ha a Számlázz.hu nem találja:
```ts
const dijbekero = await kassza.invoices.find(dijbekeroSzam)
if (dijbekero) await kassza.invoices.deleteProforma(dijbekeroSzam)
```
A `find({ orderNumber })` az adott rendelésszámú bizonylatok közül a legutóbbit adja vissza. Ez
más bizonylat is lehet ugyanezzel a rendelésszámmal, és akkor a díjbekérő törlése után sem kapsz
`null`-t.
# Minta
URL: https://www.kasszajs.hu/docs/dijbekero-torlese/minta
> Díjbekérő kiállítása, törlése rendelésszám alapján és a második törlés 335-ös hibája, a ténylegesen elküldött xmlszamladbkdel XML-lel.
A példa kiállít egy díjbekérőt, rendelésszám alapján törli, majd a számával még egyszer megpróbálja. A második kérésre 335-ös, `not_found` kategóriájú hibát kapsz, mert a díjbekérő már nincs meg. Az **Elküldött XML** fülön mindkét `xmlszamladbkdel` kérést látod: az elsőben a `rendelesszam`, a másodikban a `szamlaszam` elem azonosítja a díjbekérőt.
## XSD séma [#xsd-séma]
```text
http://www.szamlazz.hu/docs/xsds/szamladbkdel/xmlszamladbkdel.xsd
http://www.szamlazz.hu/docs/xsds/szamladbkdel/xmlszamladbkdelvalasz.xsd
```
# Nyugta létrehozás
URL: https://www.kasszajs.hu/docs/nyugta-letrehozas
> Nyugta kiállítása a kassza receipts.create() metódusával, hívásazonosítóval, forintos kerekítéssel és vegyes fizetéssel.
Nyugtát a `kassza.receipts.create()` metódussal állítasz ki. Vevőt és dátumot nem kell megadnod: elég az előtag, a fizetési mód és legalább egy tétel. A kassza kiszámolja a tételek összegeit a nyugta kerekítési szabályai szerint, összeállítja az `xmlnyugtacreate` XML-t, elküldi, és a választ típusos `Receipt` objektumként adja vissza.
```ts
import { createKassza } from 'kassza'
const kassza = createKassza({
defaults: { receipt: { prefix: 'NYGT', paymentMethod: 'bankkártya' } },
})
const nyugta = await kassza.receipts.create({
callId: `WEB-${rendeles.id}`,
orderNumber: `WEB-${rendeles.id}`,
items: [
{ name: 'Kávé', quantity: 2, grossUnitPrice: 890, vat: 27 },
{ name: 'Kifli', grossUnitPrice: 250, vat: 5 },
],
})
console.log(nyugta.number, nyugta.totals.grossAmount)
```
Minden mező: előtag, fizetési mód, pénznem, tételek, kifizetések, hívásazonosító.
A Receipt mezői, a PDF, és a nyugtakészítésnél előforduló hibák.
Futtatható példa a ténylegesen elküldött XML-lel és a Számlázz.hu válaszával.
Tételösszegek és kerekítés, PDF sablon, rendelésszám, NAV adatszolgáltatás, adattörlő kód.
A Számla Agent nyugtája számítógéppel előállított nyugta: nem pótolja az online pénztárgépet,
és nem e-nyugta. Csak nem pénztárgép-köteles tevékenységhez adható (például webshop, mozgó
vendéglátás, sok szolgáltatás), és csak olyan fiókból, ahol be van állítva a NAV Online
adatkapcsolat. A részletek és a négy feltétel, amelyek mellett nyugta adható számla helyett: [Nyugta
vagy számla?](/docs/alapok/nyugta-vagy-szamla).
A `callId` (XML-ben `hivasAzonosito`) véd a dupla nyugta ellen: ugyanazzal az azonosítóval a
Számlázz.hu nem készít második nyugtát, hanem 338-as hibát ad. A kassza ezért csak `callId`
mellett próbálja újra a nyugtakészítést hálózati hiba, időtúllépés vagy karbantartás után.
Hívásazonosító nélkül egyszer küld, és saját ciklusban akkor se küldd újra. Részletek a
[Kérés](/docs/nyugta-letrehozas/keres#hívásazonosító-callid) oldalon.
A nyugtákról 2026. szeptember 1-jétől adatot kell szolgáltatni a NAV-nak, de a NAV 2026. december
31-ig türelmi időt ad, és a Számlázz.hu szerint egyelőre nincs teendőd. Lásd
[NAV adatszolgáltatás](/docs/nyugta-letrehozas/beallitasok/nav-adatszolgaltatas). A napi
összesítőhöz, az egyeztetéshez és a kézi tartalék nyugtatömbhöz a kassza is ad eszközt: [NAV
nyugta-adatszolgáltatás](/docs/nav-nyugta).
A kész nyugtával további műveleteket végezhetsz: [sztornózhatod](/docs/nyugta-sztorno), [lekérdezheted](/docs/nyugta-lekerdezes) nyugtaszám vagy rendelésszám alapján, és [elküldheted e-mailben](/docs/nyugta-kikuldes).
Ha ugyanaz a rendelés többször is nyugtát kérhet (webhook, várólista, ismételt gombnyomás), a `receipts.createOnce()` kiállítás előtt megkeresi a nyugtát, bizonytalan kimenet után pedig visszakeresi: lásd [Pontosan egyszer](/docs/alapok/pontosan-egyszer). Ha a vevő utólag számlát kér, a `receipts.convertToInvoice()` sztornózza a nyugtát, és számlát állít ki helyette: lásd [Nyugta vagy számla?](/docs/alapok/nyugta-vagy-szamla#ha-a-vevő-utólag-számlát-kér).
# Kérés
URL: https://www.kasszajs.hu/docs/nyugta-letrehozas/keres
> A receipts.create() bemenete mezőről mezőre, az xmlnyugtacreate XML elemeivel, alapértékekkel, a hívásazonosító szerepével és a kassza által ellenőrzött szabályokkal.
```ts
interface ReceiptsApi {
create(input: CreateReceiptInput, options?: { signal?: AbortSignal }): Promise
}
```
A kassza a bemenetből `xmlnyugtacreate` XML-t épít, és `multipart/form-data` POST kérésben, az `action-szamla_agent_nyugta_create` form mezőben küldi el a `https://www.szamlazz.hu/szamla/` címre.
Kötelező az **előtag** (`prefix`), a **fizetési mód** (`paymentMethod`) és legalább egy **tétel** (`items`). Az előtagot és a fizetési módot a kliens alapbeállításaiban is megadhatod. Vevőt, eladót és dátumot a nyugtán nem adhatsz meg, a keltét a Számlázz.hu adja.
Az alábbi táblázatokban az **XML elem** oszlop a Számla Agent mezőjét mutatja, így a hivatalos
dokumentáció bármelyik mezőjét megtalálod a kasszában. A kassza az elemeket az XSD által előírt
sorrendben írja ki, az üres opcionális elemeket pedig kihagyja.
## Fejléc [#fejléc]
| Mező | XML elem | Alapérték | Leírás |
| --- | --- | --- | --- |
| `callId` | `hivasAzonosito` | – | Egyedi hívásazonosító, a dupla nyugta elleni védelem, lásd [lent](#hívásazonosító-callid). |
| `prefix` | `elotag` | – | **Kötelező.** Nyugtaszám előtag, csak nagybetű és szám, például `'NYGT'`. Lásd [Előtag](#előtag-prefix). |
| `paymentMethod` | `fizmod` | – | **Kötelező.** Fizetési mód, szabad szöveg, például `'készpénz'` vagy `'bankkártya'`. A számlával ellentétben nincs beépített alapértéke. |
| `currency` | `penznem` | `'HUF'` | Pénznem. A `'HUF'`, `'Ft'`, `'FT'`, `'huf'` és `'ft'` forintnak számít. |
| `exchangeRate` | `devizaarf` | – | Devizás nyugtán kötelező, pozitív szám. Forintos nyugtán a kassza nem küldi el. |
| `exchangeBank` | `devizabank` | – | Devizás nyugtán kötelező, az árfolyamot jegyző bank, például `'MNB'`. Forintos nyugtán a kassza nem küldi el. |
| `comment` | `megjegyzes` | – | Szabad szöveges megjegyzés, a nyugtán megjelenik. |
| `template` | `pdfSablon` | normál A4 | PDF sablon: `'A'` (normál A4), `'N'` (80 mm), `'J'` (jegy) vagy `'L'` (jegy logóval). Lásd [PDF sablon](/docs/nyugta-letrehozas/beallitasok/pdf-sablon). |
| `customerLedgerId` | `fokonyvVevo` | – | A vevő főkönyvi azonosítója a könyveléshez. |
| `orderNumber` | `rendelesSzam` | – | Rendelésszám a nyugtán. Ez alapján később le is kérdezheted a nyugtát, lásd [Rendelésszám](/docs/nyugta-letrehozas/beallitasok/rendelesszam). |
| `downloadPdf` | `pdfLetoltes` | `true` | Kérje-e a PDF-et a válaszban. A `beallitasok` blokkba kerül, a hitelesítés mellé. |
## Előtag (prefix) [#előtag-prefix]
Az előtag csak nagybetűt és számot tartalmazhat, kötőjelet, szóközt és kisbetűt nem. A kassza a hibás formátumot a küldés előtt elutasítja, így a Számlázz.hu 337-es hibája nem fordul elő.
Olyan előtag nem használható nyugtán, amelyet a fiókban már számlákhoz használsz. Ilyenkor a
Számlázz.hu 336-os hibát ad, és ezt a kassza nem tudja előre ellenőrizni. Használj külön
előtagot, például `NYGT`-t.
## Tételek (items) [#tételek-items]
Minden tételnél a `name`, a `vat` és pontosan egy ár kell: `netUnitPrice` vagy `grossUnitPrice`. A nettó, áfa és bruttó értéket a kassza számolja ki. Forintos nyugtán a bruttó egész szám, a nettó és az áfa legfeljebb 2 tizedesjegy, és a nettó és az áfa összege pontosan kiadja a bruttót. A részleteket a [Tételösszegek és kerekítés](/docs/nyugta-letrehozas/beallitasok/tetelosszegek) oldal írja le.
| Mező | XML elem | Alapérték | Leírás |
| --- | --- | --- | --- |
| `name` | `megnevezes` | – | **Kötelező.** A tétel megnevezése. |
| `identifier` | `azonosito` | – | Cikkszám vagy termékazonosító. |
| `quantity` | `mennyiseg` | `1` | Mennyiség. Nem lehet 0. |
| `unit` | `mennyisegiEgyseg` | `defaults.receipt.unit`, különben `'db'` | Mennyiségi egység. |
| `netUnitPrice` | `nettoEgysegar` | – | Nettó egységár, nettó alapú számításhoz. |
| `grossUnitPrice` | – | – | Bruttó egységár, pénztári árakhoz. A kassza ebből számolja a nettó egységárat. |
| `vat` | `afakulcs` | – | **Kötelező.** Áfakulcs, például `27`, `'AAM'`, vagy a csak nyugtán használt `'ÁKK'`, `'MAA'`, `'EU'`, `'EUK'`. |
| `netAmount`, `vatAmount`, `grossAmount` | `netto`, `afa`, `brutto` | számolt | Saját összegek. Mindhármat egyszerre kell megadni. |
| `ledger` | `fokonyv` | – | Főkönyvi adatok: `revenue` (`arbevetel`) és `vat` (`afa`). Üres objektumnál a blokk kimarad. |
| `comment` | `megjegyzes` | – | A tétel megjegyzése. |
| `dataDeletionCode` | `torloKod` | – | Adattörlő kód, nemnegatív egész szám. Lásd [Adattörlő kód](/docs/nyugta-letrehozas/beallitasok/adattorlo-kod). |
A kódos áfakulcsokat (például `'AAM'` vagy `'ÁKK'`) a kassza 0%-kal számolja, a bruttó így megegyezik a nettóval. Az összes számszerű kulcsot és kódot az [Áfakulcsok](/docs/szamla-letrehozas/beallitasok-es-szabalyok/afakulcsok) oldal sorolja fel.
Az alábbi példák a `defaults.receipt`-ben megadott előtagot és fizetési módot használják:
```ts
await kassza.receipts.create({
callId: 'PENZTAR-2026-0001',
items: [
{ name: 'Kávé', quantity: 2, grossUnitPrice: 890, vat: 27 },
{ name: 'Kifli', grossUnitPrice: 250, vat: 5 },
],
})
```
A Kávé tétel így `1401.57` nettó és `378.43` áfa, összesen `1780` bruttó lesz, `700.785` nettó egységárral.
```ts
await kassza.receipts.create({
callId: 'IRODA-2026-0042',
items: [{ name: 'Tanácsadás', quantity: 2, unit: 'óra', netUnitPrice: 10_000, vat: 27 }],
})
```
A tétel `20000.00` nettó és `5400.00` áfa, összesen `25400` bruttó. Nettó alapnál a kassza a nettót két tizedesjegyre, a bruttót egészre kerekíti, és az áfa a kettő különbsége.
```ts
await kassza.receipts.create({
callId: 'ERP-2026-0815',
items: [{ name: 'Lábtörlő', vat: 27, netAmount: 787.4, vatAmount: 212.6, grossAmount: 1000 }],
})
```
Forintos nyugtán a kassza a saját összegeket is ellenőrzi a küldés előtt, és a nettót és az áfát két tizedesjeggyel küldi: `787.40`, `212.60`, `1000`.
## Kifizetések (payments) [#kifizetések-payments]
Vegyes fizetésnél, például ha a vevő részben SZÉP kártyával, részben bankkártyával fizet, a `payments` tömbben sorolod fel a fizetési eszközöket. A mező opcionális. Ha megadod, a kifizetések összegének két tizedesjegyre kerekítve pontosan egyeznie kell a nyugta bruttó végösszegével, különben a kassza a küldés előtt hibát dob. A Számlázz.hu ugyanezt 340-es hibával jelezné.
| Mező | XML elem | Leírás |
| --- | --- | --- |
| `method` | `fizetoeszkoz` | **Kötelező.** A fizetési eszköz, például `'bankkártya'` vagy `'utalvány'`. |
| `amount` | `osszeg` | **Kötelező.** Az ezzel az eszközzel fizetett összeg. |
| `description` | `leiras` | A fizetési eszköz leírása, például `'OTP SZÉP kártya'`. |
```ts
await kassza.receipts.create({
callId: 'PENZTAR-2026-0002',
items: [
{ name: 'Kávé', quantity: 2, grossUnitPrice: 890, vat: 27 },
{ name: 'Kifli', grossUnitPrice: 250, vat: 5 },
],
payments: [
{ method: 'utalvány', amount: 1_000, description: 'OTP SZÉP kártya' },
{ method: 'bankkártya', amount: 1_030 },
],
})
```
A nyugta bruttó végösszege `1780 + 250 = 2030`, a két kifizetés együtt pontosan ennyi.
## Hívásazonosító (callId) [#hívásazonosító-callid]
A `callId` a hívás egyedi azonosítója. Ha ugyanazzal a `callId`-val újra beküldöd a nyugtát, a Számlázz.hu nem készít második nyugtát, hanem 338-as hibát ad. Ez teszi a nyugtakészítést idempotenssé.
- **Újrapróbálás:** a kassza csak `callId` mellett próbálja újra a nyugtakészítést, és csak `network`, `timeout` vagy `maintenance` hibánál. A próbálkozások száma alapból 3, a `maxAttempts` opcióval legfeljebb 5, lásd [Amit a kassza újrapróbál](/docs/alapok/hibakezeles#amit-a-kassza-újrapróbál). Hívásazonosító nélkül a kérés egyszer megy ki.
- **Ha az első kérés mégis célba ért:** előfordulhat, hogy egy időtúllépéses kérés után a nyugta elkészült, és az újrapróbálás 338-as hibát kap. Ez `duplicate` kategóriájú hiba, a nyugta ilyenkor már létezik.
- **Honnan legyen:** a saját rendelésazonosítódból képezd, és ugyanezt add meg `orderNumber`-nek is. A Számla Agent hívásazonosító alapján nem kérdez le, rendelésszám alapján viszont igen.
```ts
import { type CreateReceiptInput, isSzamlazzError, type Receipt } from 'kassza'
async function nyugtaz(rendelesId: string, items: CreateReceiptInput['items']): Promise {
const azonosito = `WEB-${rendelesId}`
try {
return await kassza.receipts.create({ callId: azonosito, orderNumber: azonosito, items })
} catch (error) {
if (isSzamlazzError(error) && error.isDuplicate) {
const meglevo = await kassza.receipts.find({ orderNumber: azonosito })
if (meglevo) return meglevo
}
throw error
}
}
```
A `find()` `null`-t ad, ha nincs ilyen nyugta. Ha a fiókban engedélyezed, hogy egy rendelésszám több nyugtán is szerepeljen, a rendelésszám nem azonosít egyértelműen egy nyugtát, ezért ehhez a mintához tartsd egyedinek.
## Devizás nyugta [#devizás-nyugta]
Ha a `currency` nem forint, az `exchangeRate` és az `exchangeBank` is kötelező. A számlával ellentétben nyugtánál nincs `'MNB'` alapértelmezés a bankra.
```ts
await kassza.receipts.create({
callId: 'MUZEUM-2026-0077',
currency: 'EUR',
exchangeRate: 395.5,
exchangeBank: 'MNB',
items: [{ name: 'Múzeumi belépő', quantity: 2, grossUnitPrice: 12.5, vat: 27 }],
})
```
Devizás nyugtán a nettó, az áfa és a bruttó is két tizedesjegyre kerekedik, a forintos szabályok (egész bruttó, pontos összegegyezés) nem vonatkoznak rá. A pénznemkódokat a [Támogatott devizanemek](/docs/szamla-letrehozas/beallitasok-es-szabalyok/penznemek) oldal sorolja fel.
## Amit a kassza a küldés előtt ellenőriz [#amit-a-kassza-a-küldés-előtt-ellenőriz]
Ezeknél a hibáknál `validation` kategóriájú `SzamlazzError` jön, és kérés nem megy a Számlázz.hu-hoz:
- hiányzik az előtag, vagy nem csak nagybetűből és számból áll (337),
- hiányzik a fizetési mód,
- nincs tétel, egy tételnek nincs neve, a mennyisége 0, vagy egy összege nem érvényes szám,
- ismeretlen áfakulcs, vagy a `netUnitPrice` és a `grossUnitPrice` egyszerre, vagy egyik sincs megadva,
- saját összegeknél nincs meg mindhárom érték,
- forintos nyugtán a bruttó nem egész (363), a nettó vagy az áfa 2-nél több tizedesjegyet tartalmaz (364, 365), a nettó és az áfa összege nem pontosan a bruttó (261), vagy a nettó egységár × mennyiség a nettótól, illetve a nettó × áfakulcs / 100 az áfától 2-nél többel eltér (259, 260),
- devizás nyugtán hiányzik vagy nem pozitív az árfolyam, vagy hiányzik a bank,
- ismeretlen PDF sablon,
- az adattörlő kód nem nemnegatív egész szám,
- egy kifizetésnek nincs fizetési eszköze, érvénytelen az összege, vagy a kifizetések összege eltér a bruttó végösszegtől (340).
A zárójeles szám azt a Számlázz.hu hibakódot jelzi, amelyet a kassza ellenőrzése nélkül kapnál. Tételhibánál az üzenet a tétel sorszámát és nevét is tartalmazza.
## Alapértékek a kliensben [#alapértékek-a-kliensben]
A gyakran ismétlődő mezőket nem kell minden hívásnál megadni. A `createKassza({ defaults: { receipt } })` alapértékei akkor érvényesülnek, ha a hívásban az adott mező hiányzik:
```ts
const kassza = createKassza({
defaults: {
receipt: {
prefix: 'NYGT',
paymentMethod: 'bankkártya',
template: 'N',
unit: 'db',
},
},
})
```
Alapértelmezhető a `prefix`, a `paymentMethod`, a `currency`, az `exchangeRate`, az `exchangeBank`, a `downloadPdf`, a `template`, a `unit` és a `customerLedgerId`. A `callId`, az `orderNumber`, a `comment` és a `payments` minden nyugtánál más, ezeknek nincs alapértéke. A táblázatot a [Kliens beállítása](/docs/alapok/kliens-beallitasa#alapértelmezések-nyugtára) oldal is tartalmazza.
# Válasz
URL: https://www.kasszajs.hu/docs/nyugta-letrehozas/valasz
> A Receipt mezői, honnan jönnek az xmlnyugtavalasz válaszból, a PDF mentése, és a nyugtakészítésnél jellemző hibák.
Sikeres nyugtakészítés után a `create()` egy `Receipt` objektummal tér vissza. Ugyanezt a típust adja a [sztornó](/docs/nyugta-sztorno) és a [lekérdezés](/docs/nyugta-lekerdezes) is.
```ts
const nyugta = await kassza.receipts.create(adatok)
await db.rendeles.update({
where: { id: rendelesId },
data: { nyugtaszam: nyugta.number, brutto: nyugta.totals.grossAmount },
})
```
## Receipt [#receipt]
A fejléc mezői a válasz `` blokkjának `` eleméből jönnek.
| Mező | Típus | Forrás a válaszban | Leírás |
| --- | --- | --- | --- |
| `id` | `number` | `id` | A nyugta belső azonosítója a Számlázz.hu-ban. |
| `number` | `string` | `nyugtaszam` | A nyugtaszám, például `'NYGT-2026-1'`. Ha hiányzik, `unexpected_response` hibát kapsz. |
| `callId` | `string \| undefined` | `hivasAzonosito` | A kérésben megadott hívásazonosító. |
| `type` | `'receipt' \| 'reversal'` | `tipus` | `NY` esetén `'receipt'`, `SN` (sztornó nyugta) esetén `'reversal'`. |
| `isReversed` | `boolean` | `stornozott` | `true`, ha a nyugtát sztornózták. |
| `reversedReceiptNumber` | `string \| undefined` | `stornozottNyugtaszam` | Sztornó nyugtánál az eredeti nyugta száma. |
| `issueDate` | `string` | `kelt` | A nyugta kelte `'YYYY-MM-DD'` alakban. |
| `paymentMethod` | `string` | `fizmod` | Fizetési mód. |
| `currency` | `string` | `penznem` | Pénznem. Ha a válaszból hiányzik, `'HUF'`. |
| `exchangeRate` | `number \| undefined` | `devizaarf` | Árfolyam devizás nyugtán. |
| `exchangeBank` | `string \| undefined` | `devizabank` | Az árfolyamot jegyző bank. |
| `comment` | `string \| undefined` | `megjegyzes` | A nyugta megjegyzése. |
| `customerLedgerId` | `string \| undefined` | `fokonyvVevo` | A vevő főkönyvi azonosítója. |
| `isTest` | `boolean` | `teszt` | `true`, ha a nyugta tesztfiókban készült. |
| `orderNumber` | `string \| undefined` | `rendelesSzam` | Rendelésszám. |
| `items` | `ReceiptItem[]` | `tetelek` | A tételek a Számlázz.hu által rögzített összegekkel, lásd lent. |
| `payments` | `ReceiptPayment[]` | `kifizetesek` | A kifizetések. Ha nem adtál meg, üres tömb. |
| `totals` | `ReceiptTotals` | `osszegek` | Végösszeg és áfakulcsonkénti bontás. |
| `pdf` | `Uint8Array \| undefined` | `nyugtaPdf` | A nyugta PDF-je, Base64-ből dekódolva. `downloadPdf: false` esetén nincs. |
### ReceiptItem [#receiptitem]
| Mező | Forrás a válaszban | Leírás |
| --- | --- | --- |
| `name` | `megnevezes` | A tétel megnevezése. |
| `identifier` | `azonosito` | Cikkszám vagy termékazonosító. |
| `quantity` | `mennyiseg` | Mennyiség. |
| `unit` | `mennyisegiEgyseg` | Mennyiségi egység. |
| `netUnitPrice` | `nettoEgysegar` | Nettó egységár. |
| `vat` | `afatipus`, ha van, különben `afakulcs` | Kódos áfakulcsnál a kód, például `'ÁKK'`, egyébként a szám, például `27`. |
| `vatPercentage` | `afakulcs` | Az áfa mértéke számként. |
| `netAmount` | `netto` vagy `nettoErtek` | Nettó érték. |
| `vatAmount` | `afa` vagy `afaErtek` | Áfa érték. |
| `grossAmount` | `brutto` vagy `bruttoErtek` | Bruttó érték. |
| `ledger` | `fokonyv` | `{ revenue, vat }`, ha a tételnek van főkönyvi adata. |
A Számlázz.hu a tételösszegeket hol `netto`, hol `nettoErtek` néven adja vissza, a kassza mindkettőt olvassa. A tétel megjegyzése és adattörlő kódja nem szerepel a válaszban.
### ReceiptPayment és ReceiptTotals [#receiptpayment-és-receipttotals]
| Mező | Forrás a válaszban | Leírás |
| --- | --- | --- |
| `payments[].method` | `fizetoeszkoz` | A fizetési eszköz. |
| `payments[].amount` | `osszeg` | Az ezzel az eszközzel fizetett összeg. |
| `payments[].description` | `leiras` | A fizetési eszköz leírása. |
| `totals.netAmount`, `vatAmount`, `grossAmount` | `totalossz` | Nettó, áfa és bruttó végösszeg. Ha a válaszban nincs `totalossz`, a kassza a tételekből összegzi. |
| `totals.byVat` | `afakulcsossz` | Áfakulcsonkénti bontás, elemenként `vat`, `vatPercentage`, `netAmount`, `vatAmount` és `grossAmount`. |
A [mintában](/docs/nyugta-letrehozas/minta) szereplő kéttételes nyugtánál a `totals` értéke `1639.67` nettó, `390.33` áfa és `2030` bruttó, a `byVat` pedig egy 27%-os és egy 5%-os sort tartalmaz.
A nyugtakészítés válasza mindig `xmlnyugtavalasz` XML. A kassza előbb a `szlahu_error`
fejléceket és a szöveges `[ERR]` hibát nézi, utána a `sikeres` elemet. Ha az `false`, a
`hibakod` és a `hibauzenet` alapján `SzamlazzError`-t dob. Ha a válasz nem XML, vagy nem jelez
sikert, `unexpected_response` hibát kapsz, 5xx HTTP státusznál pedig `network` hibát.
## A PDF mentése [#a-pdf-mentése]
A `pdf` egy `Uint8Array`, amit közvetlenül fájlba írhatsz, feltölthetsz tárhelyre, vagy visszaadhatsz HTTP válaszként:
```ts
import { writeFile } from 'node:fs/promises'
if (nyugta.pdf) await writeFile(`nyugtak/${nyugta.number}.pdf`, nyugta.pdf)
```
```ts title="app/nyugtak/[szam]/route.ts"
import { kassza } from '@/lib/kassza'
export async function GET(_request: Request, { params }: RouteContext<'/nyugtak/[szam]'>) {
const { szam } = await params
const nyugta = await kassza.receipts.find(szam)
if (!nyugta?.pdf) return new Response('Nincs ilyen nyugta.', { status: 404 })
return new Response(nyugta.pdf, {
headers: {
'Content-Type': 'application/pdf',
'Content-Disposition': `inline; filename="${szam}.pdf"`,
},
})
}
```
```ts
import { receiptPdfKey, s3FetchStorage, storePdf } from 'kassza/storage'
const tarhely = s3FetchStorage({
bucket: 'nyugtak',
region: 'auto',
endpoint: process.env.R2_ENDPOINT,
accessKeyId: process.env.R2_ACCESS_KEY_ID ?? '',
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY ?? '',
})
if (nyugta.pdf) {
const kulcs = receiptPdfKey({ number: nyugta.number, date: nyugta.issueDate })
await storePdf(tarhely, kulcs, nyugta.pdf)
}
```
A `receiptPdfKey()` `nyugtak/ÉÉÉÉ/HH/nyugtaszám.pdf` alakú kulcsot képez. A PDF később is lekérhető a `kassza.receipts.get()` hívással, lásd [Nyugta lekérdezés](/docs/nyugta-lekerdezes), e-mailben pedig a [Nyugta kiküldés](/docs/nyugta-kikuldes) művelettel küldheted el.
## Hibák [#hibák]
Ha a nyugta nem készült el, a `create()` `SzamlazzError`-t dob. A nyugtakészítésnél leggyakoribb hibák:
| Kód | Kategória | Mikor fordul elő? |
| --- | --- | --- |
| – | `validation` | A kassza a küldés előtt hibát talált a bemenetben, lásd [Kérés](/docs/nyugta-letrehozas/keres#amit-a-kassza-a-küldés-előtt-ellenőriz). |
| 3 | `auth` | Hibás Agent kulcs. |
| 57 | `validation` | Az XML nem felel meg az XSD-nek. |
| 71, 152 | `duplicate` | A rendelésszám már szerepel nyugtán, és a fiókban a nyugtákra be van kapcsolva a rendelésszám ismétlődés tiltása. |
| 136 | `account` | Lejárt előfizetés vagy rendezetlen díj. |
| 259–261 | `validation` | Hibás tételösszeg. A 261-est forintos nyugtán akkor kapod, ha a nettó és az áfa összege nem pontosan a bruttó. |
| 336 | `validation` | Az előtagot a fiókban már számlákhoz használod, nyugtához másik kell. |
| 337 | `validation` | Az előtag nem csak nagybetűből és számból áll. A kassza küldés előtt kiszűri. |
| 338 | `duplicate` | Ezzel a `callId`-val már készült nyugta. **Ne állítsd ki újra.** |
| 340 | `validation` | A kifizetések összege eltér a bruttó végösszegtől. A kassza küldés előtt kiszűri. |
| 363–365 | `validation` | Forintos nyugtán a bruttó nem egész, vagy a nettó, illetve az áfa 2-nél több tizedesjegyet tartalmaz. |
| 537–539 | `validation`, `account` | Adattörlő kód hiba: 400-nál több kód egy tételen, tesztfiók, vagy kikapcsolt beállítás. |
| – | `network`, `timeout` | Nem tudni, elkészült-e a nyugta. `callId` mellett a kassza már újrapróbálta. |
Az összes kódot a [Hibakezelés, hibakódok](/docs/alapok/hibakezeles#hibakódok) oldal sorolja fel.
A 338-as hibánál a kassza a `hint` mezőben leírja a következő lépést: ha adtál meg rendelésszámot, a lekérdezést javasolja, különben a Számlázz.hu felületére irányít, mert a Számla Agent hívásazonosító alapján nem kérdez le.
Hálózati hiba, időtúllépés, 338-as vagy 71/152-es hiba után a nyugta már létezhet. Mielőtt újra
kiállítanád, hívd meg a `kassza.receipts.find({ orderNumber })` metódust. Ha újra küldöd, ugyanazzal
a `callId`-val tedd, így nem készülhet második nyugta.
# Minta
URL: https://www.kasszajs.hu/docs/nyugta-letrehozas/minta
> Futtatható nyugtakészítési példa a kassza kódjával, a ténylegesen elküldött xmlnyugtacreate XML-lel és a Számlázz.hu válaszával, valamint az XSD séma.
Az alábbi példa a [sandboxban](/sandbox) futott. A **Kód** fül a kassza hívást, az **Elküldött XML** fül a Számla Agentnek küldött `xmlnyugtacreate` dokumentumot, a **Válasz** fül a szimulált Számlázz.hu választ mutatja. A kód fölé húzva az egeret a típusokat is látod.
## Pénztári nyugta [#pénztári-nyugta]
Két tétel bruttó egységárral, két különböző áfakulccsal. Az előtag és a fizetési mód a kliens alapbeállításaiból jön, a hívásazonosító és a rendelésszám ugyanaz.
Az elküldött XML-ben látszik a forintos nyugta kerekítése. A 27%-os Kávé tétel bruttója egész `1780`, a nettója `1401.57`, az áfája `378.43`, a kettő összege pontosan a bruttó. A nettó egységár `700.785`, mert 2 × 700.785 adja ki pontosan a nettót. Az 5%-os Kifli tételnél a kassza a nettót és az áfát két tizedesjeggyel küldi (`238.10`, `11.90`). A `pdfLetoltes` értéke `true`, mert a `downloadPdf` alapból be van kapcsolva.
## Az XML felépítése [#az-xml-felépítése]
A kassza az elemeket az XSD sorrendjében írja ki, az üres opcionális elemeket kihagyja:
| Blokk | Tartalom |
| --- | --- |
| `beallitasok` | Az Agent kulcs és a `pdfLetoltes`. |
| `fejlec` | `hivasAzonosito`, `elotag`, `fizmod`, `penznem`, `devizaarf`, `devizabank`, `megjegyzes`, `pdfSablon`, `fokonyvVevo`, `rendelesSzam`. |
| `tetelek` | Tételenként `megnevezes`, `azonosito`, `mennyiseg`, `mennyisegiEgyseg`, `nettoEgysegar`, `afakulcs`, `netto`, `afa`, `brutto`, `fokonyv`, `megjegyzes`, `torloKod`. |
| `kifizetesek` | Csak akkor, ha megadtad a `payments` mezőt. |
A mezők jelentését a [Kérés](/docs/nyugta-letrehozas/keres) oldal írja le.
## Vegyes fizetés [#vegyes-fizetés]
Ha a fenti nyugtát részben SZÉP kártyával, részben bankkártyával fizetik, a `payments` mezővel adod meg a megosztást:
```ts
await kassza.receipts.create({
callId: 'PENZTAR-2026-0002',
orderNumber: 'PENZTAR-2026-0002',
items: [
{ name: 'Kávé', quantity: 2, grossUnitPrice: 890, vat: 27 },
{ name: 'Kifli', grossUnitPrice: 250, vat: 5 },
],
payments: [
{ method: 'utalvány', amount: 1_000, description: 'OTP SZÉP kártya' },
{ method: 'bankkártya', amount: 1_030 },
],
})
```
Az XML végére a tételek után ez a blokk kerül:
```xml
utalvány1000OTP SZÉP kártyabankkártya1030
```
A két összeg együtt pontosan a nyugta `2030` Ft-os bruttó végösszege. Ha eltérne, a kassza el sem küldené a kérést.
## XSD séma [#xsd-séma]
A kassza az XML-t a Számlázz.hu `xmlnyugtacreate.xsd` sémája szerint építi fel. A séma itt érhető el:
```text
https://www.szamlazz.hu/szamla/docs/xsds/nyugtacreate/xmlnyugtacreate.xsd
```
A kassza tesztjei a generált XML-t a letölthető XSD-vel is összevetik, ezért a sémával neked nem kell foglalkoznod. Ha a hivatalos dokumentációban olyan mezőt találsz, amely a kasszában még nincs meg, [nyiss egy issue-t](https://github.com/futozs/kassza/issues).
## További példák [#további-példák]
Hívásazonosító és rendelésszám, lekérdezés duplikált hívás után.
Euróban kiállított nyugta árfolyammal és bankkal.
Nyugta kiállítása, majd sztornó nyugta a hivatkozással.
A kész nyugta elküldése e-mailben.
# Nyugta beállítások és szabályok
URL: https://www.kasszajs.hu/docs/nyugta-letrehozas/beallitasok
> Tételösszegek és kerekítés, PDF sablon, rendelésszám és hívásazonosító, NAV adatszolgáltatás és adattörlő kód a nyugtán.
A nyugtakészítés szabályai: mit ellenőriz a Számla Agent, mit számol és ellenőriz helyetted a kassza, és melyik fiókbeállítás hat a nyugtára.
Egész bruttó, 2 tizedes nettó és áfa, pontos összegegyezés, kalkulátorral.
A4, 80 mm-es és jegy sablon, PDF letöltése a válaszban.
Rendelésszám ismétlődés tiltása nyugtán, dupla nyugta elleni védelem callId-val.
Kötelező nyugta-adatszolgáltatás 2026. szeptember 1-jétől, türelmi idővel.
A tételek torloKod mezője és a korlátai.
# Tételösszegek és kerekítés
URL: https://www.kasszajs.hu/docs/nyugta-letrehozas/beallitasok/tetelosszegek
> Forintos nyugtán a bruttó egész, a nettó és az áfa legfeljebb 2 tizedes, és az összegük pontosan a bruttó. Így számol a kassza, és ezt jelentik a 261-es és a 363–365-ös hibák.
Forintos nyugtán szigorúbbak a tételösszegek szabályai, mint a számlán. Forintosnak számít a nyugta, ha a `currency` üres (ez az alapérték), `'HUF'` vagy `'Ft'`. Ilyenkor:
- a bruttó érték egész szám, mert a nyugtát egész forintban fizetik ki,
- a nettó és az áfa értéke legfeljebb 2 tizedesjegy lehet,
- a nettó és az áfa összegének tűrés nélkül ki kell adnia a bruttót, a Számlázz.hu kerekítés nélkül hasonlít,
- a nettó egységár × mennyiség legfeljebb 2 Ft-tal térhet el a nettó értéktől, a nettó × áfakulcs / 100 pedig az áfa értéktől.
Ezt nem kell kézzel kiszámolnod. Megadod a `grossUnitPrice` vagy a `netUnitPrice` mezőt és az áfakulcsot, a kassza pedig a nyugtára érvényes szabályokkal számolja ki a nettó, az áfa és a bruttó értéket.
A kalkulátorban a Bizonylat kapcsolót állítsd Nyugtára, így ugyanazt látod, ami a nyugtára kerül.
## Bruttó alapon [#bruttó-alapon]
Nyugtánál ez a gyakoribb eset, mert a vevő bruttó árat fizet.
1. bruttó érték = bruttó egységár × mennyiség, egészre kerekítve,
2. áfa érték = bruttó érték × áfakulcs / (100 + áfakulcs), 2 tizedesre kerekítve,
3. nettó érték = bruttó érték − áfa érték,
4. a nettó egységárat a kassza visszaszámolja: a legkevesebb tizedesjegyű (legfeljebb 6) értéket választja, amely a mennyiséggel szorozva kiadja a nettó értéket.
Például 3 darab 890 Ft-os kávé 27%-os áfával: bruttó 2 670 Ft, áfa 567,64 Ft, nettó 2 102,36 Ft, nettó egységár 700,787 Ft. Ugyanez számlán 568 Ft áfa és 2 102 Ft nettó lenne, mert ott a nettó és az áfa is egész.
Tört bruttó egységárnál a bruttó érték egészre kerekedik: 199,50 Ft-ból 200 Ft lesz, 42,52 Ft áfával és 157,48 Ft nettóval.
## Nettó alapon [#nettó-alapon]
Mivel a bruttónak nettó alapon is egésznek kell lennie, a sorrend más, mint a számlán:
1. nettó érték = nettó egységár × mennyiség, 2 tizedesre kerekítve,
2. bruttó érték = nettó érték × (1 + áfakulcs / 100), egészre kerekítve,
3. áfa érték = bruttó érték − nettó érték.
Például 3 darab 99,99 Ft nettó egységárú tétel 27%-os áfával: nettó 299,97 Ft, bruttó 380,9619 helyett 381 Ft, áfa 81,03 Ft. Az áfa így nem pontosan a nettó 27%-a (80,99 Ft), de a 2 Ft-os tűrésen belül marad, és a nettó és az áfa összege pontosan 381.
## Devizás nyugta [#devizás-nyugta]
A fenti forintos szabályok devizás nyugtára nem vonatkoznak. Devizában a kassza a nettó, az áfa és a bruttó értéket is legfeljebb 2 tizedesre kerekíti, ugyanúgy, mint a devizás számlán.
## Ellenőrzés küldés előtt [#ellenőrzés-küldés-előtt]
Forintos nyugtánál a kassza a kérés elküldése előtt ellenőrzi a tételösszegeket. Ha valami nem stimmel, `validation` kategóriájú `SzamlazzError`-t dob, a kérés el sem jut a Számlázz.hu-hoz. A hibaüzenet megnevezi a tételt, és zárójelben a megfelelő hibakódot:
| Kód az üzenetben | Mi a hiba |
| --- | --- |
| 261 | A nettó és az áfa összege nem pontosan a bruttó |
| 363 | A bruttó nem egész szám |
| 364 | A nettó 2-nél több tizedesjegyet tartalmaz |
| 365 | Az áfa 2-nél több tizedesjegyet tartalmaz |
| 259 | A nettó egységár × mennyiség 2 Ft-nál többel eltér a nettótól |
| 260 | Az áfa 2 Ft-nál többel eltér a nettó × áfakulcs / 100 értéktől |
A Számlázz.hu a 261-es és a 363–365-ös kódot adja vissza, ha ilyen tétel mégis eljut hozzá. Lásd még: [Hibakezelés](/docs/alapok/hibakezeles).
## Saját összegek [#saját-összegek]
Ha a tételösszegeket máshonnan kapod, a `netAmount`, `vatAmount` és `grossAmount` mezővel felülírhatod a számolást. Mindhármat meg kell adnod. A számlával ellentétben forintos nyugtán a kassza ezeket is ellenőrzi, ezért már kerekített értékeket adj át:
```ts
items: [
{ name: 'Ajándékkártya', vat: 27, netAmount: 787.4, vatAmount: 212.6, grossAmount: 1_000 },
]
```
Egy másik rendszerből változtatás nélkül átvett, kerekítetlen érték, például a `netAmount: 787.40157480315`, 364-es hivatkozású `validation` hibát ad, mert a nettó 2-nél több tizedesjegyet tartalmaz.
## Áfakulcsok nyugtán [#áfakulcsok-nyugtán]
Nyugtán a számlánál használható összes áfakulcs megadható, lásd [Áfakulcsok](/docs/szamla-letrehozas/beallitasok-es-szabalyok/afakulcsok). Ezeken felül a kassza négy olyan kódot is elfogad, amely csak nyugtán használható:
| `vat` | Számolás |
| --- | --- |
| `'ÁKK'` | 0%-os kulcsként |
| `'MAA'` | 0%-os kulcsként |
| `'EU'` | 0%-os kulcsként |
| `'EUK'` | 0%-os kulcsként |
A kód változatlanul kerül a kérés `afakulcs` mezőjébe. TypeScriptben a nyugtatételek `vat` mezőjének típusa a `ReceiptVatRate`, amely ezt a négy kódot is tartalmazza.
## Számolás a kassza nélkül [#számolás-a-kassza-nélkül]
A nyugta kerekítése külön is elérhető a `kassza/money` modulból, például ha a pénztárfelületen ugyanazt az összeget akarod mutatni, ami a nyugtára kerül:
```ts
import { calculateReceiptItem, summarizeItems } from 'kassza/money'
const tetelek = [
calculateReceiptItem({ quantity: 2, grossUnitPrice: 890, vat: 27 }),
calculateReceiptItem({ grossUnitPrice: 250, vat: 5 }),
]
const { grossAmount, byVat } = summarizeItems(tetelek)
```
Itt a `grossAmount` 2 030, a `byVat` pedig áfakulcsonként bontja a nettó, az áfa és a bruttó értéket. Devizás nyugtához a pénznemet második paraméterként add át: `calculateReceiptItem(tetel, 'EUR')`.
A `calculateReceiptItem` nem ismeri az `'ÁKK'`, `'MAA'`, `'EU'` és `'EUK'` kódot, ezekre
„Ismeretlen áfakulcs” hibát dob. Ilyen tételnél a számoláshoz add át a `vat: 0` értéket, ahogy
a `receipts.create()` is teszi.
Lásd még: [Kerekítés számlán](/docs/szamla-letrehozas/beallitasok-es-szabalyok/kerekites) és [Pénzszámítás](/docs/kiegeszitok/penzszamitas).
# PDF sablon
URL: https://www.kasszajs.hu/docs/nyugta-letrehozas/beallitasok/pdf-sablon
> A nyugta PDF sablonjának kiválasztása a template mezővel ('A', 'N', 'J', 'L'), és a PDF letöltése a downloadPdf opcióval.
A `template` mező (XML-ben `pdfSablon`) azt választja ki, milyen formátumú PDF készüljön a nyugtáról. Opcionális: ha nem adod meg, normál A4-es nyugta készül.
| `template` | Sablon a Számlázz.hu felületén |
| --- | --- |
| `'A'` | normál (A4) |
| `'N'` | 80 mm-es |
| `'J'` | jegy sablon |
| `'L'` | jegy sablon logóval |
```ts
const nyugta = await kassza.receipts.create({
callId: 'PENZTAR-2026-0001',
template: 'N',
items: [{ name: 'Kávé', quantity: 2, grossUnitPrice: 890, vat: 27 }],
})
```
Ha minden nyugtád ugyanazzal a sablonnal készül, add meg alapbeállításként:
```ts
const kassza = createKassza({
defaults: { receipt: { prefix: 'NYGT', paymentMethod: 'készpénz', template: 'N' } },
})
```
A `template` típusa `ReceiptPdfTemplate`, és a kassza küldés előtt is ellenőrzi: más érték `validation` hibát ad, így elírt sablonkód nem jut el a Számlázz.hu-hoz.
## Sztornó és lekérdezés [#sztornó-és-lekérdezés]
A `receipts.reverse()`, a `receipts.get()` és a `receipts.find()` is fogad `template` mezőt, ugyanezzel a négy értékkel:
```ts
const sztorno = await kassza.receipts.reverse({ receiptNumber: 'NYGT-2026-1', template: 'N' })
const nyugta = await kassza.receipts.get({ receiptNumber: 'NYGT-2026-1', template: 'N' })
```
A `defaults.receipt.template` csak a `receipts.create()` hívásra hat. Sztornónál és lekérdezésnél a
sablont a hívásban add meg.
## PDF letöltése [#pdf-letöltése]
A `downloadPdf` (XML-ben `pdfLetoltes`) szabályozza, hogy a válasz tartalmazza-e a nyugta PDF-jét. A kasszában az alapértéke `true`, ekkor a PDF a visszakapott nyugta `pdf` mezőjében van, `Uint8Array`-ként. Ha nem kell a PDF, kapcsold ki, így kisebb a válasz:
```ts
const nyugta = await kassza.receipts.create({
callId: 'PENZTAR-2026-0002',
downloadPdf: false,
items: [{ name: 'Napijegy', grossUnitPrice: 4_500, vat: 27 }],
})
```
A `downloadPdf` a `create()` mellett a `reverse()`, a `get()` és a `find()` hívásnak is megadható, alapbeállításként viszont szintén csak a `create()`-re hat. A `pdf` mező akkor is üres lehet, ha a válasz nem tartalmaz PDF-et, ezért használat előtt ellenőrizd:
```ts
import { receiptPdfKey, storePdf } from 'kassza/storage'
if (nyugta.pdf) {
await storePdf(tarhely, receiptPdfKey({ number: nyugta.number }), nyugta.pdf)
}
```
Lásd még: [Nyugta lekérdezés](/docs/nyugta-lekerdezes) és [Számlasablon és nyelvek](/docs/szamla-letrehozas/beallitasok-es-szabalyok/szamlasablon-es-nyelvek).
# Rendelésszám és hívásazonosító
URL: https://www.kasszajs.hu/docs/nyugta-letrehozas/beallitasok/rendelesszam
> Az orderNumber mező a nyugtán, a nyugtákra vonatkozó „Rendelésszám ismétlődés tiltása” beállítás, és a callId, amely a dupla nyugta ellen véd.
Az `orderNumber` (XML-ben a fejléc `rendelesSzam` eleme) a saját rendszered rendelésazonosítója. Opcionális, ha kihagyod, a nyugtán nem jelenik meg rendelésszám. Érdemes mégis megadni, mert ezzel kérdezheted le később a nyugtát.
```ts
const nyugta = await kassza.receipts.create({
callId: 'PENZTAR-2026-0212',
orderNumber: 'PENZTAR-2026-0212',
items: [{ name: 'Napijegy', grossUnitPrice: 4_500, vat: 27 }],
})
const ugyanaz = await kassza.receipts.get({ orderNumber: 'PENZTAR-2026-0212' })
```
A válaszban a rendelésszám változatlanul visszajön, a kasszában a nyugta `orderNumber` mezőjében.
## Rendelésszám ismétlődés tiltása [#rendelésszám-ismétlődés-tiltása]
A Számlázz.hu fiókban beállíthatod, hogy egy rendelésszám csak egy nyugtán szerepelhessen. A kapcsoló helye: Beállítások → Fiók beállításai → Számlázás beállítások, a Nyugtaszerkesztő beállítások szekcióban, „Rendelésszám ismétlődés tiltása” néven.
- **Bekapcsolva:** korábban már használt rendelésszámmal nem készíthető új nyugta, a kérés hibával elbukik.
- **Kikapcsolva:** ugyanazzal a rendelésszámmal több nyugta is kiállítható, például ha egy rendelést több részletben nyugtázol.
A nyugtákra ez a kapcsoló vonatkozik, a számlák azonos nevű beállításától függetlenül, amely a Számlaszerkesztő beállítások szekcióban van. Az ellenőrzés bizonylattípusonként történik, így egy nyugtán és egy számlán szerepelhet ugyanaz a rendelésszám. A számlák szabályait a [Rendelésszám és duplikáció](/docs/szamla-letrehozas/beallitasok-es-szabalyok/rendelesszam) oldal írja le.
Ha rendelésszám alapján kérdezed le a nyugtákat, kapcsold be a tiltást. Csak így biztos, hogy egy
rendelésszám pontosan egy nyugtát azonosít.
## Hívásazonosító és idempotencia [#hívásazonosító-és-idempotencia]
A dupla nyugta ellen a fiókbeállítástól függetlenül a `callId` (XML-ben `hivasAzonosito`) véd. Ha megadod, egyedinek kell lennie: ugyanazzal a `callId`-val ismételt kérés nem hoz létre második nyugtát, hanem 338-as, `duplicate` kategóriájú hibát ad.
A kassza erre építve csak `callId` mellett próbálja újra a nyugtakészítést, és csak `network`, `timeout` vagy `maintenance` hibánál. Hívásazonosító nélkül a kérés egyszer megy ki, és egy időtúllépés után nem tudhatod, elkészült-e a nyugta.
A Számla Agent hívásazonosító alapján nem kérdez le, rendelésszám alapján igen. Ezért a bevált minta: a `callId` és az `orderNumber` legyen ugyanaz a saját rendelésazonosítódból képzett érték, és `duplicate` hibánál rendelésszám alapján kérd le a már elkészült nyugtát.
```ts
import { type CreateReceiptInput, isSzamlazzError, type Receipt } from 'kassza'
async function nyugtaz(rendelesId: string, items: CreateReceiptInput['items']): Promise {
const azonosito = `PENZTAR-${rendelesId}`
try {
return await kassza.receipts.create({ callId: azonosito, orderNumber: azonosito, items })
} catch (error) {
if (isSzamlazzError(error) && error.isDuplicate) {
const meglevo = await kassza.receipts.find({ orderNumber: azonosito })
if (meglevo) return meglevo
}
throw error
}
}
```
A nyugtakészítést ne csomagold saját újrapróbáló ciklusba. A Számlázz.hu kitilthatja a fiókot az
ismételten beküldött kérések miatt, az újrapróbálást a kassza biztonságosan elvégzi helyetted.
A teljes leírást az újrapróbálás részleteivel a [Kérés](/docs/nyugta-letrehozas/keres#hívásazonosító-callid) oldal hívásazonosítóról szóló része tartalmazza.
# NAV adatszolgáltatás
URL: https://www.kasszajs.hu/docs/nyugta-letrehozas/beallitasok/nav-adatszolgaltatas
> A nyugták kötelező NAV adatszolgáltatása 2026. szeptember 1-jétől, a 2026. december 31-ig tartó türelmi idő, és mit jelent ez a kassza használatakor.
A Számla Agenttel kiállított nyugta számítógéppel előállított nyugtának számít. Ezekről 2026. szeptember 1-jétől adatot kell szolgáltatni a NAV felé.
A NAV az év végéig türelmi időt ad, addig nincs teendőd. A Számlázz.hu az adatszolgáltatás
automatizálásán dolgozik, és értesíti a felhasználókat, amikor valamit be kell majd állítani.
## Mit jelent ez a kasszában? [#mit-jelent-ez-a-kasszában]
Jelenleg semmit nem kell módosítanod a kódodon. A `receipts.create()` ugyanúgy működik, mint eddig, új mezőt vagy beállítást nem kell megadnod.
Ha a Számlázz.hu új beállítást vagy kérésmezőt vezet be, és ez a kasszát is érinti, a változást a [változásnaplóban](https://github.com/futozs/kassza/blob/main/CHANGELOG.md) követheted. A türelmi idő vége előtt érdemes ránézned, és szükség esetén frissítened a csomagot.
A nyugta-adatszolgáltatás részleteit és a gyakori kérdéseket a Számlázz.hu tudástárának erről szóló cikke tartalmazza.
## Amiben a kassza segít [#amiben-a-kassza-segít]
A beküldést a Számlázz.hu vállalja a nála kiállított nyugtákra, a NAV összekötés után. Ami neked marad, ahhoz a kassza ad eszközt:
- a napi összesítő kiszámítása a kiállított nyugtákból, ugyanazokkal a szabályokkal, mint a NAV ([Napi összesítő](/docs/nav-nyugta/napi-osszesito)),
- annak ellenőrzése, hogy a NAV-hoz minden napod megérkezett ([Egyeztetés](/docs/nav-nyugta/egyeztetes)),
- a tartalék kézi nyugtatömb összesítője, amelyet neked kell beküldeni ([Kézi nyugtatömb](/docs/nav-nyugta/kezi-nyugtatomb)).
Az áttekintés a [NAV nyugta-adatszolgáltatás](/docs/nav-nyugta) oldalon van.
Lásd még: [Nyugta létrehozás: kérés](/docs/nyugta-letrehozas/keres).
# Adattörlő kód
URL: https://www.kasszajs.hu/docs/nyugta-letrehozas/beallitasok/adattorlo-kod
> A nyugtatételek dataDeletionCode mezője (torloKod), a szükséges fiókbeállítás, a korlátok és a kapcsolódó hibakódok.
Nyugtán is minden tételhez megadható adattörlő kód a `dataDeletionCode` mezőben (XML-ben a `tetel` elem `torloKod` eleme). Opcionális, az értéke 0 vagy annál nagyobb egész szám. A kassza küldés előtt ezt ellenőrzi, más értékre `validation` hibát dob.
```ts
await kassza.receipts.create({
callId: 'PENZTAR-2026-0301',
items: [
{ name: 'Kávé', quantity: 2, grossUnitPrice: 890, vat: 27, dataDeletionCode: 123 },
{ name: 'Kifli', grossUnitPrice: 250, vat: 5 },
],
})
```
Ha egy tételnél nem adod meg, a kérésbe nem kerül `torloKod` elem.
## Feltételek és hibák [#feltételek-és-hibák]
| Feltétel | Ha nem teljesül |
| --- | --- |
| A Számlázz.hu fiók Beállítások menüjében be kell kapcsolni az adattörlő kód használatát | 539-es hiba (`account`) |
| Egy tételhez legfeljebb 400 adattörlő kód tartozhat | 537-es hiba (`validation`) |
| Demo- és tesztfiókban nem használható | 538-as hiba (`account`) |
Az adattörlő kód API-n keresztüli és tömeges használatáról a Számlázz.hu tudástára ír részletesen. Számlán ugyanez a mező működik, lásd [Adattörlő kód számlán](/docs/szamla-letrehozas/beallitasok-es-szabalyok/adattorlo-kod).
# Nyugta sztornó
URL: https://www.kasszajs.hu/docs/nyugta-sztorno
> Kiállított nyugta érvénytelenítése sztornó nyugtával a kassza receipts.reverse() metódusával.
Egy kiállított nyugtát a `kassza.receipts.reverse()` érvénytelenít. A Számlázz.hu ilyenkor egy új, `SN` típusú sztornó nyugtát készít, amely hivatkozik az eredetire, az eredeti nyugta pedig sztornózott állapotba kerül. Elég a nyugtaszámot átadnod.
```ts
const sztorno = await kassza.receipts.reverse('NYGT-2026-107')
```
A sztornó mezői: nyugtaszám, hívásazonosító, PDF letöltés és sablon.
A sztornó nyugta Receipt objektuma és a sztornónál előforduló hibák.
Nyugta kiállítása és sztornózása a sandboxban.
A kassza a sztornót csak `callId` megadásával próbálja újra karbantartás,
hálózati hiba vagy időtúllépés után. Nélküle az első hiba hozzád kerül, és
neked kell kiderítened, elkészült-e a sztornó.
# Kérés
URL: https://www.kasszajs.hu/docs/nyugta-sztorno/keres
> A receipts.reverse() bemenete, a Számla Agent xmlnyugtast XML elemeivel és alapértékekkel.
```ts
kassza.receipts.reverse(input: string | ReverseReceiptInput, options?: { signal?: AbortSignal }): Promise
```
A kassza `xmlnyugtast` XML-t küld az `action-szamla_agent_nyugta_storno` form mezőben. A legegyszerűbb hívás csak a sztornózandó nyugta számát kapja:
```ts
await kassza.receipts.reverse('NYGT-2026-107')
```
Ha hívásazonosítót, más PDF sablont adnál, vagy nem kérnéd a PDF-et, objektumot adj át:
```ts
await kassza.receipts.reverse({
receiptNumber: 'NYGT-2026-107',
callId: 'PENZTAR-107-STORNO',
template: 'N',
downloadPdf: false,
})
```
## Mezők [#mezők]
| Mező | XML elem | Alapérték | Leírás |
| --- | --- | --- | --- |
| `receiptNumber` | `fejlec/nyugtaszam` | – | **Kötelező.** A sztornózandó nyugta száma. |
| `callId` | `fejlec/hivasAzonosito` | – | A sztornó kérés egyedi hívásazonosítója. Csak ezzel próbálja újra a kassza a kérést. |
| `downloadPdf` | `beallitasok/pdfLetoltes` | `true` | Kérje-e a sztornó nyugta PDF-jét a válaszban. |
| `template` | `fejlec/pdfSablon` | – | A sztornó nyugta PDF sablonja: `A` (normál A4), `N` (80 mm), `J` (jegy) vagy `L` (jegy logóval). |
A kassza a nyugtaszám és a `callId` elejéről és végéről levágja a szóközöket. Más mezőt a kérés nem fogad: a sztornó nyugta tételeit és összegeit a Számlázz.hu az eredeti nyugtából állítja össze.
A `createKassza({ defaults: { receipt } })` alapbeállításai a sztornóra nem hatnak. Ha a `downloadPdf` vagy a `template` értékét módosítanád, itt add meg.
## Szabályok [#szabályok]
- Csak normál nyugta sztornózható. Már sztornózott nyugtát, vagy magát a sztornó nyugtát a Számlázz.hu elutasítja.
- Üres nyugtaszámnál vagy ismeretlen PDF sablonnál a kassza `validation` hibát dob, és a kérés el sem indul.
- A kassza a sztornót **csak `callId` megadásával** próbálja újra, és csak `maintenance`, `network` vagy `timeout` hibánál. Az üres vagy csak szóközből álló `callId` nem számít.
- A sztornónak adj saját `callId`-t, például az eredeti hívásazonosítót `-STORNO` végződéssel. A már létező hívásazonosítót a Számlázz.hu `338`-as hibával utasítja el.
# Válasz
URL: https://www.kasszajs.hu/docs/nyugta-sztorno/valasz
> A sztornó nyugta Receipt objektuma, amelyet a receipts.reverse() visszaad, és a sztornózásnál jellemző hibák.
Sikeres sztornó után a `reverse()` egy `Receipt` objektumot ad vissza, ugyanazt a típust, mint a [nyugta létrehozás](/docs/nyugta-letrehozas). A mezők a Számlázz.hu `xmlnyugtavalasz` válaszából jönnek, és a **sztornó nyugtát** írják le, nem az eredetit.
| Mező | Típus | Forrás | Leírás |
| --- | --- | --- | --- |
| `number` | `string` | `alap/nyugtaszam` | A sztornó nyugta száma. |
| `type` | `'receipt' \| 'reversal'` | `alap/tipus` | Sztornónál `'reversal'` (`SN`). |
| `reversedReceiptNumber` | `string \| undefined` | `alap/stornozottNyugtaszam` | Az eredeti, sztornózott nyugta száma. |
| `isReversed` | `boolean` | `alap/stornozott` | A sztornó nyugtán `false`. |
| `callId` | `string \| undefined` | `alap/hivasAzonosito` | A sztornó kérés hívásazonosítója. |
| `id` | `number` | `alap/id` | A sztornó nyugta belső azonosítója. |
| `issueDate` | `string` | `alap/kelt` | A sztornó kelte, `YYYY-MM-DD` formában. |
| `items` | `ReceiptItem[]` | `tetelek/tetel` | Az eredeti tételek, negatív mennyiséggel és összegekkel. |
| `totals` | `ReceiptTotals` | `osszegek` | A sztornó nettó, áfa és bruttó végösszege, negatív értékekkel. |
| `pdf` | `Uint8Array \| undefined` | `nyugtaPdf` | A sztornó nyugta PDF-je, ha a `downloadPdf` nem `false`. |
A többi mező (`paymentMethod`, `currency`, `payments`, `comment`, `orderNumber`, `isTest` és a többi) ugyanazt jelenti, mint a nyugta létrehozásánál.
```ts
const sztorno = await kassza.receipts.reverse({
receiptNumber: 'NYGT-2026-107',
callId: 'PENZTAR-107-STORNO',
})
console.log(sztorno.reversedReceiptNumber, sztorno.totals.grossAmount)
if (sztorno.pdf) await writeFile(`nyugtak/${sztorno.number}.pdf`, sztorno.pdf)
```
Hogy az eredeti nyugta sztornózva van-e, a [Nyugta lekérdezés](/docs/nyugta-lekerdezes) válaszának `isReversed` mezője mutatja.
## Hibák [#hibák]
| Kód | Kategória | Mikor fordul elő? |
| --- | --- | --- |
| – | `validation` | Üres nyugtaszám vagy ismeretlen PDF sablon. A kérés el sem indul. |
| 3 | `auth` | Hibás Agent kulcs. |
| 7, 339 | `not_found` | A Számlázz.hu nem sztornózza a nyugtát: nincs ilyen nyugtaszám, a nyugtát már sztornózták, vagy maga is sztornó nyugta. Az okot a `message` tartalmazza. |
| 338 | `duplicate` | A `callId` már létezik. Lehet, hogy egy korábbi próbálkozás már elkészítette a sztornót. |
| 1 | `maintenance` | Karbantartás. `callId` megadásával a kassza újrapróbálja. |
| – | `network`, `timeout` | Nem tudni, elkészült-e a sztornó. `callId` megadásával a kassza újrapróbálja, nélküle vagy az utolsó próbálkozás után ellenőrizd az eredeti nyugtát. |
A `not_found` itt nem mindig jelenti, hogy a nyugta nem létezik. Ha a sztornó hibát ad, kérdezd le az eredeti nyugtát, és nézd meg, sztornózva van-e már:
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.receipts.reverse({ receiptNumber: nyugtaszam, callId: `${nyugtaszam}-STORNO` })
} catch (error) {
if (
isSzamlazzError(error) &&
(error.isNotFound ||
error.isDuplicate ||
error.category === 'network' ||
error.category === 'timeout')
) {
const eredeti = await kassza.receipts.find(nyugtaszam)
if (eredeti?.isReversed) return
}
throw error
}
```
# Minta
URL: https://www.kasszajs.hu/docs/nyugta-sztorno/minta
> Nyugta kiállítása és sztornózása hívásazonosítóval, a ténylegesen elküldött xmlnyugtast XML-lel.
A példa kiállít egy nyugtát két mozijegyről, majd saját `callId`-val sztornózza. A **Válasz** fülön látod a sztornó nyugta számát, a hivatkozást az eredetire és a negatív bruttó végösszeget. Az **Elküldött XML** fülön mindkét kérés ott van, köztük a sztornó `xmlnyugtast` dokumentuma.
## XSD séma [#xsd-séma]
Az XML elemeinek sorrendje kötött, a kassza az XSD sorrendjében írja őket.
```text
https://www.szamlazz.hu/szamla/docs/xsds/nyugtast/xmlnyugtast.xsd
```
# Nyugta lekérdezés
URL: https://www.kasszajs.hu/docs/nyugta-lekerdezes
> Kiállított nyugta adatainak és PDF-jének lekérése nyugtaszám vagy rendelésszám alapján a kassza receipts.get() és receipts.find() metódusával.
Egy már kiállított nyugta adatait a nyugtaszám vagy a létrehozáskor megadott rendelésszám alapján kérheted le, alapból a PDF-fel együtt. A kasszában erre két metódus van: a `receipts.get()` hibát dob, ha nincs ilyen nyugta, a `receipts.find()` ilyenkor `null`-t ad.
```ts
const nyugta = await kassza.receipts.get('NYGT-2026-1')
const rendeleshez = await kassza.receipts.find({ orderNumber: 'PENZTAR-2026-0212' })
```
Azonosítás nyugtaszámmal vagy rendelésszámmal, PDF és sablon.
A Receipt mezői, a tételek, az összegek és a lekérdezés hibái.
Nyugta kiállítása és lekérdezése a sandboxban.
A Számla Agent a nyugtát csak nyugtaszám vagy rendelésszám alapján adja vissza, a `callId`
alapján nem. Ha egy nyugtát később a saját rendelésedhez akarsz visszakeresni, a
[létrehozásnál](/docs/nyugta-letrehozas) adj meg `orderNumber`-t is.
# Kérés
URL: https://www.kasszajs.hu/docs/nyugta-lekerdezes/keres
> A receipts.get() és a receipts.find() bemenete, a Számla Agent xmlnyugtaget XML elemeivel és alapértékekkel.
```ts
kassza.receipts.get(input: string | GetReceiptInput, options?: { signal?: AbortSignal }): Promise
kassza.receipts.find(input: string | GetReceiptInput, options?: { signal?: AbortSignal }): Promise
```
A két metódus ugyanazt a bemenetet fogadja, és ugyanazt az `xmlnyugtaget` XML-t küldi az `action-szamla_agent_nyugta_get` form mezőben. Ha csak a nyugtaszámod van meg, szövegként add át:
```ts
const nyugta = await kassza.receipts.get('NYGT-2026-1')
```
Rendelésszám alapján, vagy ha a PDF-re nincs szükséged, objektumot adj át:
```ts
const nyugta = await kassza.receipts.get({
orderNumber: 'PENZTAR-2026-0212',
downloadPdf: false,
})
```
## Mezők [#mezők]
| Mező | XML elem | Alapérték | Leírás |
| --- | --- | --- | --- |
| `receiptNumber` | `fejlec/nyugtaszam` | – | A nyugta száma, amit a létrehozáskor kaptál. Szöveges bemenetnél ez kerül ide. |
| `orderNumber` | `fejlec/rendelesSzam` | – | A nyugta létrehozásakor megadott rendelésszám. |
| `callId` | `fejlec/hivasAzonosito` | – | Opcionális hívásazonosító. A nyugtát nem ez alapján keresi. |
| `template` | `fejlec/pdfSablon` | – | A PDF sablonja: `A` (normál A4), `N` (80 mm), `J` (jegy), `L` (jegy logóval). |
| `downloadPdf` | `beallitasok/pdfLetoltes` | `true` | Kérje-e a nyugta PDF-jét a válaszban. |
A `receiptNumber` és az `orderNumber` közül pontosan az egyiket kell megadnod. A `beallitasok` blokkba a kassza az Agent kulcsot is beírja.
## Szabályok [#szabályok]
- Ha egyik azonosítót sem adod meg, vagy mindkettőt megadod, a kassza küldés előtt `validation` hibát dob. A `GetReceiptInput` típus a két mező együttes megadását már fordításkor sem engedi.
- A kassza a szöveges mezők elejéről és végéről levágja a szóközöket. Az üres vagy csak szóközből álló érték hiányzónak számít.
- Ismeretlen `template` értékre szintén `validation` hiba jön, még a küldés előtt.
- A rendelésszám csak akkor azonosít egyértelműen egy nyugtát, ha a fiókban tiltva van a rendelésszám ismétlődése.
- A lekérdezés nem hoz létre semmit, ezért a kassza **magától újrapróbálja** karbantartás, hálózati hiba és időtúllépés után, alapból háromszor. Lásd [Amit a kassza újrapróbál](/docs/alapok/hibakezeles#amit-a-kassza-újrapróbál).
# Válasz
URL: https://www.kasszajs.hu/docs/nyugta-lekerdezes/valasz
> A Receipt mezői, amelyeket a receipts.get() és a receipts.find() visszaad, a Számlázz.hu xmlnyugtavalasz elemeivel, és a lekérdezésnél jellemző hibák.
A lekérdezés válasza ugyanolyan, mint a nyugta létrehozásáé: a `get()` egy `Receipt` objektumot ad vissza, a `find()` pedig `Receipt`-et vagy `null`-t. A mezők a Számlázz.hu `xmlnyugtavalasz` válaszának `nyugta` blokkjából jönnek, a PDF a `nyugtaPdf` elemből.
```ts
const nyugta = await kassza.receipts.find({ orderNumber: 'PENZTAR-2026-0212' })
if (nyugta && !nyugta.isReversed) {
console.log(nyugta.number, nyugta.totals.grossAmount)
}
```
## Receipt [#receipt]
| Mező | Típus | Forrás a válaszban | Leírás |
| --- | --- | --- | --- |
| `id` | `number` | `alap/id` | A nyugta Számlázz.hu azonosítója. |
| `number` | `string` | `alap/nyugtaszam` | A nyugta száma, például `'NYGT-2026-1'`. |
| `callId` | `string \| undefined` | `alap/hivasAzonosito` | A létrehozáskor megadott hívásazonosító. |
| `type` | `'receipt' \| 'reversal'` | `alap/tipus` | `NY` esetén `'receipt'`, `SN` (sztornó nyugta) esetén `'reversal'`. |
| `isReversed` | `boolean` | `alap/stornozott` | `true`, ha a nyugtát sztornózták. |
| `reversedReceiptNumber` | `string \| undefined` | `alap/stornozottNyugtaszam` | Sztornó nyugtánál az eredeti nyugta száma. |
| `issueDate` | `string` | `alap/kelt` | A nyugta kelte, ahogy a Számlázz.hu küldi. |
| `paymentMethod` | `string` | `alap/fizmod` | Fizetési mód, például `'készpénz'`. |
| `currency` | `string` | `alap/penznem` | Pénznem, ahogy a Számlázz.hu küldi. Ha hiányzik, `'HUF'`. |
| `exchangeBank` | `string \| undefined` | `alap/devizabank` | Az árfolyamot jegyző bank. |
| `exchangeRate` | `number \| undefined` | `alap/devizaarf` | Az árfolyam. |
| `comment` | `string \| undefined` | `alap/megjegyzes` | Megjegyzés a nyugtán. |
| `customerLedgerId` | `string \| undefined` | `alap/fokonyvVevo` | A vevő főkönyvi azonosítója. |
| `isTest` | `boolean` | `alap/teszt` | `true`, ha a nyugta tesztfiókban készült. |
| `orderNumber` | `string \| undefined` | `alap/rendelesSzam` | A nyugta rendelésszáma. |
| `items` | `ReceiptItem[]` | `tetelek/tetel` | A nyugta tételei. |
| `payments` | `ReceiptPayment[]` | `kifizetesek/kifizetes` | A kifizetések. Ha nincsenek, üres tömb. |
| `totals` | `ReceiptTotals` | `osszegek` | A végösszeg és az áfakulcsonkénti összesítés. |
| `pdf` | `Uint8Array \| undefined` | `nyugtaPdf` | A nyugta PDF-je. `downloadPdf: false` esetén nincs. |
Ha egy nem opcionális mező hiányzik a válaszból, a kassza alapértéket ad neki: szövegnél üres szöveget, számnál `0`-t, logikai mezőnél `false`-t. Ha a válaszból a `nyugta` blokk vagy a nyugtaszám hiányzik, a kassza `unexpected_response` hibát dob.
### ReceiptItem [#receiptitem]
| Mező | Típus | Forrás a válaszban | Leírás |
| --- | --- | --- | --- |
| `name` | `string` | `megnevezes` | A tétel neve. |
| `identifier` | `string \| undefined` | `azonosito` | A tétel azonosítója. |
| `quantity` | `number` | `mennyiseg` | Mennyiség. |
| `unit` | `string` | `mennyisegiEgyseg` | Mennyiségi egység. |
| `netUnitPrice` | `number` | `nettoEgysegar` | Nettó egységár. |
| `vat` | `ReceiptVatRate` | `afatipus`, `afakulcs` | Az áfatípus, ha van (például `'ÁKK'`), különben a számos kulcs (például `27`). |
| `vatPercentage` | `number` | `afakulcs` | Az áfakulcs számként. |
| `netAmount` | `number` | `netto` vagy `nettoErtek` | Nettó érték. |
| `vatAmount` | `number` | `afa` vagy `afaErtek` | Áfa érték. |
| `grossAmount` | `number` | `brutto` vagy `bruttoErtek` | Bruttó érték. |
| `ledger` | `{ revenue?, vat? } \| undefined` | `fokonyv/arbevetel`, `fokonyv/afa` | Főkönyvi azonosítók, ha vannak. |
### ReceiptPayment [#receiptpayment]
| Mező | Típus | Forrás a válaszban | Leírás |
| --- | --- | --- | --- |
| `method` | `string` | `fizetoeszkoz` | Fizetőeszköz, például `'utalvány'`. |
| `amount` | `number` | `osszeg` | A kifizetett összeg. |
| `description` | `string \| undefined` | `leiras` | Leírás. |
### ReceiptTotals [#receipttotals]
| Mező | Típus | Forrás a válaszban | Leírás |
| --- | --- | --- | --- |
| `netAmount` | `number` | `osszegek/totalossz/netto` | Nettó végösszeg. |
| `vatAmount` | `number` | `osszegek/totalossz/afa` | Áfa végösszeg. |
| `grossAmount` | `number` | `osszegek/totalossz/brutto` | Bruttó végösszeg. |
| `byVat` | `array` | `osszegek/afakulcsossz` | Áfakulcsonként `vat`, `vatPercentage`, `netAmount`, `vatAmount` és `grossAmount`. |
Ha a válaszban nincs `totalossz`, a kassza a végösszegeket a tételekből adja össze, két tizedesre kerekítve.
## A PDF mentése [#a-pdf-mentése]
A `pdf` egy `Uint8Array`, amit közvetlenül fájlba írhatsz:
```ts
import { writeFile } from 'node:fs/promises'
const nyugta = await kassza.receipts.get('NYGT-2026-1')
if (nyugta.pdf) await writeFile(`nyugtak/${nyugta.number}.pdf`, nyugta.pdf)
```
Ha csak az adatokra van szükséged, add meg a `downloadPdf: false` opciót, így a válasz is kisebb.
## Hibák [#hibák]
| Kód | Kategória | Mikor fordul elő? |
| --- | --- | --- |
| – | `validation` | Nincs megadva azonosító, mindkettő meg van adva, vagy ismeretlen a sablon. A kérés el sem indul. |
| 1 | `maintenance` | Karbantartás. A kassza magától újrapróbálja. |
| 3 | `auth` | Hibás Agent kulcs. |
| 339 | `not_found` | Nincs ilyen nyugta. A `find()` erre `null`-t ad. |
| – | `network`, `timeout` | A kassza magától újrapróbálja, és csak az utolsó sikertelen próbálkozás után dobja a hibát. |
| – | `unexpected_response` | A válaszból hiányzik a `nyugta` blokk vagy a nyugtaszám. |
A `find()` minden `not_found` kategóriájú hibánál `null`-t ad, minden más hibát továbbdob. Az összes kódot a [Hibakezelés, hibakódok](/docs/alapok/hibakezeles#hibakódok) oldal sorolja fel.
## Elkészült már a nyugta? [#elkészült-már-a-nyugta]
Ha a `receipts.create()` `duplicate` (338), `network` vagy `timeout` hibával áll le, a nyugta már létezhet. Mielőtt újra kiállítanád, keresd meg a rendelésszám alapján:
```ts
import { type CreateReceiptInput, isSzamlazzError } from 'kassza'
const BIZONYTALAN = ['duplicate', 'network', 'timeout']
async function nyugtaRendeleshez(adatok: CreateReceiptInput & { orderNumber: string }) {
try {
return await kassza.receipts.create(adatok)
} catch (error) {
if (isSzamlazzError(error) && BIZONYTALAN.includes(error.category)) {
const meglevo = await kassza.receipts.find({ orderNumber: adatok.orderNumber })
if (meglevo) return meglevo
}
throw error
}
}
```
A Számla Agent hívásazonosító alapján nem ad vissza nyugtát. Ha a létrehozásnál csak `callId`-t
adtál meg, a meglévő nyugtát a Számlázz.hu felületén kell megkeresned.
# Minta
URL: https://www.kasszajs.hu/docs/nyugta-lekerdezes/minta
> Nyugta kiállítása és lekérdezése nyugtaszám és rendelésszám alapján, a find() nem létező nyugtára, a ténylegesen elküldött xmlnyugtaget XML-lel.
A példa a [sandboxban](/sandbox) fut. Kiállít egy nyugtát rendelésszámmal, lekérdezi a nyugtaszám alapján, majd a rendelésszám alapján PDF nélkül. Végül a `find()` egy nem létező nyugtaszámot keres, amire `null` jön. Az **Elküldött XML** fülön az `xmlnyugtaget` kéréseket is látod.
## XSD séma [#xsd-séma]
A kassza az XML-t a Számlázz.hu `xmlnyugtaget.xsd` sémája szerint építi fel:
```text
https://www.szamlazz.hu/szamla/docs/xsds/nyugtaget/xmlnyugtaget.xsd
```
A `rendelesSzam` elem a letölthető sémában is szerepel, így a rendelésszámos kérés az XSD szerint is érvényes.
# Nyugta kiküldés
URL: https://www.kasszajs.hu/docs/nyugta-kikuldes
> Kiállított nyugta elküldése e-mailben a vevőnek a kassza receipts.send() metódusával.
A nyugta kiállítása nem küld e-mailt. Ha a vevő kéri a nyugtát, egy külön kéréssel küldöd ki neki, a már kiállított nyugta száma alapján. A kasszában ezt a `receipts.send()` végzi.
```ts
await kassza.receipts.send({ receiptNumber: nyugta.number, emails: 'vevo@example.hu' })
```
A kiküldés mezői: nyugtaszám, címzettek, válaszcím, tárgy, szöveg.
Mit ad vissza a send(), és milyen hibák fordulnak elő.
Nyugta kiállítása és kiküldése két címzettnek a sandboxban.
A kassza a kiküldést soha nem küldi el újra magától, mert minden sikeres kérés újabb levelet
jelent a vevőnek. Hálózati hiba után te döntsd el, kell-e még egyszer küldeni.
# Kérés
URL: https://www.kasszajs.hu/docs/nyugta-kikuldes/keres
> A receipts.send() bemenete, a Számla Agent xmlnyugtasend XML elemeivel, alapértékekkel és a kliensoldali ellenőrzéssel.
```ts
kassza.receipts.send(input: SendReceiptInput, options?: { signal?: AbortSignal }): Promise
```
A kassza `xmlnyugtasend` XML-t küld az `action-szamla_agent_nyugta_send` form mezőben. Csak már kiállított nyugtát küldhetsz ki, ezért a nyugtaszámot a `receipts.create()` vagy a `receipts.get()` válaszából veszed:
```ts
await kassza.receipts.send({ receiptNumber: nyugta.number, emails: 'vevo@example.hu' })
```
Több címzettnek, saját tárggyal és szöveggel:
```ts
await kassza.receipts.send({
receiptNumber: 'NYGT-2026-1',
emails: ['vevo@example.hu', 'konyveles@example.hu'],
replyTo: 'bolt@example.hu',
subject: 'A nyugtád',
text: 'Kedves Vásárló!\n\nCsatoltuk a nyugtát.',
})
```
## Mezők [#mezők]
| Mező | XML elem | Alapérték | Leírás |
| --- | --- | --- | --- |
| `receiptNumber` | `fejlec/nyugtaszam` | – | **Kötelező.** A kiküldendő nyugta száma. |
| `emails` | `emailKuldes/email` | – | **Kötelező.** A címzett, címek tömbje, vagy vesszővel, pontosvesszővel elválasztott lista. |
| `replyTo` | `emailKuldes/emailReplyto` | – | Válaszcím: ha a vevő válaszol a levélre, ide érkezik. |
| `subject` | `emailKuldes/emailTargy` | `Nyugta ` | A levél tárgya. |
| `text` | `emailKuldes/emailSzoveg` | – | A levél szövege. A sortöréseket változatlanul küldi. |
A címzetteket a kassza egyetlen `email` elembe fűzi, vesszővel elválasztva. A tárgyban és a szövegben nyugodtan használhatsz `&` vagy `<` karaktert, az XML escape-elést a kassza végzi.
## Szabályok [#szabályok]
- Az `emails` mező nem hagyható el, legalább egy címzett kell. A Számla Agent címzett nélkül az előző levelet küldené újra, a kassza ezt a módot nem használja.
- Tárgyat a kassza mindig küld: ha a `subject` hiányzik vagy üres, `Nyugta ` lesz. A hivatalos hibaminta szerint tárgy nélkül 7-es hibát kapnál.
- Ha a `text` üres vagy csak szóközből áll, az `emailSzoveg` elem kimarad.
- A `receiptNumber`, a címek, a `replyTo` és a `subject` elejéről és végéről a szóközöket levágja. A `text` szövegét nem módosítja.
- A kassza a kiküldést **nem próbálja újra** magától, mert egy második kérés újabb levelet küldhet a vevőnek.
## Kliensoldali ellenőrzés [#kliensoldali-ellenőrzés]
Ezek a hibák még a kérés elküldése előtt keletkeznek, `validation` kategóriával és hibakód nélkül.
| Eset | Hibaüzenet |
| --- | --- |
| Üres `receiptNumber` | Add meg a nyugtaszámot (receiptNumber). |
| Nincs egyetlen cím sem, például `[]` vagy `' , '` | Adj meg legalább egy címzett e-mail címet (emails). |
| Hibás cím az `emails` listában | Érvénytelen e-mail cím a(z) emails mezőben: … |
| Hibás `replyTo` | Érvénytelen e-mail cím a(z) replyTo mezőben: … |
A kassza csak az egyszerű `nev@domain.hu` alakot fogadja el: a címben nem lehet szóköz, vessző, pontosvessző, `<` vagy `>`, és a domainben kell pont. A `Kovács Anna ` alak és az `admin@localhost` cím ezért hibát ad.
# Válasz
URL: https://www.kasszajs.hu/docs/nyugta-kikuldes/valasz
> Mit jelent, ha a receipts.send() hiba nélkül visszatér, hogyan olvassa a kassza az xmlnyugtasendvalasz választ, és milyen hibák fordulnak elő.
A `send()` visszatérési értéke `void`. Ha hiba nélkül lefut, a Számlázz.hu sikeresnek jelezte a kiküldést. Minden más esetben `SzamlazzError`-t dob.
```ts
await kassza.receipts.send({ receiptNumber: nyugta.number, emails: vevo.email })
```
## Hogyan olvassa a kassza a választ? [#hogyan-olvassa-a-kassza-a-választ]
A Számlázz.hu `xmlnyugtasendvalasz` XML-lel válaszol, három elemmel:
| XML elem | Típus | Mit kezd vele a kassza |
| --- | --- | --- |
| `sikeres` | `boolean` | Ha `true`, és nincs hibakód, a `send()` hiba nélkül visszatér. |
| `hibakod` | `int` | A `SzamlazzError` `code` mezője lesz. |
| `hibauzenet` | `string` | A hibaüzenet, `[kód]` előtaggal, például `[7] Hiányzó adat: emailtargy elem.` |
A kassza a `szlahu_error` és `szlahu_error_code` fejlécben, illetve `[ERR]` kezdetű szöveges válaszban érkező hibát is felismeri. Az `xmlagentresponse=DONE` szöveges választ sikernek veszi.
## Hibák [#hibák]
| Kód | Kategória | Mikor fordul elő? |
| --- | --- | --- |
| – | `validation` | Hiányzó nyugtaszám, nincs címzett, vagy hibás e-mail cím. A kérés el sem indul, lásd [Kérés](/docs/nyugta-kikuldes/keres#kliensoldali-ellenőrzés). |
| 3 | `auth` | Hibás Agent kulcs. |
| 339 | `not_found` | Nincs ilyen nyugtaszám. |
| – | `unexpected_response` | A válasz se sikert, se hibakódot nem jelez. Nem tudni, kiment-e a levél. |
| – | `network`, `timeout` | Nem tudni, kiment-e a levél. |
Hálózati hiba, időtúllépés vagy váratlan válasz után a levél kimehetett. A kasszában nincs
lekérdezés, amely megmutatná, hogy egy nyugtát kiküldtek-e, ezért egy újabb `send()` dupla
levelet jelenthet. Az `error.retryable` ilyenkor `true`, de ez csak azt mondja, hogy a hiba
átmeneti: a `send()` ettől függetlenül egyszer próbálkozik.
A nyugta a kiküldés hibájától függetlenül kiállított marad. Ezért a kiküldés hibája általában ne állítsa meg a rendelés feldolgozását, elég naplózni és később kézzel ránézni:
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.receipts.send({ receiptNumber: nyugta.number, emails: vevo.email })
} catch (error) {
if (!isSzamlazzError(error)) throw error
await kikuldesiHibaMentese(nyugta.number, error.category, error.message)
}
```
# Minta
URL: https://www.kasszajs.hu/docs/nyugta-kikuldes/minta
> Nyugta kiállítása és kiküldése két címzettnek, a ténylegesen elküldött xmlnyugtasend XML-lel.
A példa kiállít egy nyugtát `callId`-val, majd kiküldi két címzettnek saját válaszcímmel, tárggyal és szöveggel. Az **Elküldött XML** fülön mindkét kérést látod, köztük a kiküldés `xmlnyugtasend` dokumentumát: a két cím egyetlen `email` elemben, vesszővel elválasztva szerepel.
## XSD séma [#xsd-séma]
```text
https://www.szamlazz.hu/szamla/docs/xsds/nyugtasend/xmlnyugtasend.xsd
```
# Adószám lekérdezés
URL: https://www.kasszajs.hu/docs/adoszam-lekerdezes
> Magyar adózó adatainak lekérdezése a NAV-tól törzsszám vagy adószám alapján, a kassza taxpayer.query() metódusával.
A Számla Agenten keresztül a NAV Online Számla rendszerétől is kérdezhetsz: egy törzsszámról megtudod, hogy érvényes-e az adószám, és ha igen, megkapod az adózó nevét, teljes adószámát és címeit. A kasszában ezt a `taxpayer.query()` végzi. Akkor hasznos, ha a vevő csak az adószámát írja be, a számla többi adatát pedig a válaszból töltöd ki.
```ts
const ceg = await kassza.taxpayer.query('12345676-2-41')
if (ceg.valid) console.log(ceg.name, ceg.address?.formatted)
```
Az elfogadott adószám formátumok, az xmltaxpayer XML és az előellenőrzés.
A TaxpayerInfo és a TaxpayerAddress mezői, a vevő kitöltése és a hibák.
Adószám lekérdezése és a vevő összerakása a sandboxban.
## Gyorsítótár [#gyorsítótár]
Egy űrlapon ugyanazt az adószámot sokszor lekérdezheted (gépelés, újratöltés, visszalépés), és minden lekérdezés egy Számla Agent kérés. A `taxpayerCache` beállítással a kassza egy közös tárolóban megjegyzi az eredményt:
```ts
import { upstashRedisStore } from 'kassza/stores'
const kassza = createKassza({
taxpayerCache: { store: upstashRedisStore(Redis.fromEnv()) },
})
```
Az érvényes adózót alapból egy napig (`ttlSeconds`), az érvénytelen adószámot 10 percig (`invalidTtlSeconds`) őrzi. A hibát (például hálózati hibát) nem jegyzi meg. A kulcs a törzsszám, így a `12345678` és a `12345678-2-42` ugyanazt a bejegyzést használja. Ha a tároló nem érhető el, a kassza a Számlázz.hu-tól kérdez, és figyelmeztet.
A `taxpayer.query()` az Agent kulcsoddal hív, ezért böngészőből ne használd. A böngészőben csak az
adószám formátumát ellenőrizd a `kassza/validators` modullal, lásd
[Előellenőrzés az űrlapon](/docs/adoszam-lekerdezes/keres#előellenőrzés-az-űrlapon).
# Kérés
URL: https://www.kasszajs.hu/docs/adoszam-lekerdezes/keres
> A taxpayer.query() bemenete, az elfogadott adószám formátumok, a Számla Agent xmltaxpayer XML-je és az előellenőrzés a kassza/validators modullal.
```ts
kassza.taxpayer.query(taxNumber: string, options?: { signal?: AbortSignal }): Promise
```
A metódus egyetlen szöveget vár: a törzsszámot vagy a teljes adószámot. A kassza kiveszi belőle a szóközöket, a kötőjeleket és az elején álló `HU` előtagot, majd az első 8 számjegyet, a törzsszámot küldi el.
| Bemenet | Formátum | Elküldött `torzsszam` |
| --- | --- | --- |
| `'12345676'` | 8 jegyű törzsszám | `12345676` |
| `'12345676-2-41'` | 11 jegyű adószám kötőjelekkel | `12345676` |
| `'12345676241'` | 11 jegyű adószám elválasztás nélkül | `12345676` |
| `'HU12345676'` | közösségi adószám | `12345676` |
Minden más bemenetre, például 7 vagy 9 számjegyre, betűre vagy nem szöveg értékre, a kassza `validation` kategóriájú `SzamlazzError`-t dob, és kérést sem küld.
## Az elküldött XML [#az-elküldött-xml]
A kassza `xmltaxpayer` XML-t küld az `action-szamla_agent_taxpayer` form mezőben:
```xml
az-agent-kulcsod12345676
```
| Mező | XML elem | Leírás |
| --- | --- | --- |
| `taxNumber` | `torzsszam` | **Kötelező.** Az adószám első 8 számjegye. |
| a kliens `agentKey` opciója | `beallitasok/szamlaagentkulcs` | Az Agent kulcs, vagy a `SZAMLAZZ_AGENT_KEY` környezeti változó. Felhasználónév és jelszó esetén a `felhasznalo` és a `jelszo` elem kerül ide. |
| `options.signal` | – | `AbortSignal` a kérés megszakításához, például ha a felhasználó közben átírta az adószámot. |
## Szabályok [#szabályok]
- A NAV csak a törzsszámot kapja meg, így az áfakódot és a megyekódot nem ellenőrzi. Ha a vevő teljes adószámot írt be, vesd össze a válasz `taxNumber` mezőjével.
- A kassza csak a formátumot nézi, a törzsszám ellenőrző számjegyét nem. Ezt a `kassza/validators` modul végzi, lásd lent.
- Lekérdezésről van szó, ezért a kassza `maintenance`, `network` és `timeout` hibánál magától újrapróbálja, alapból háromszor, 1, majd 2 másodperc várakozással. Lásd [Amit a kassza újrapróbál](/docs/alapok/hibakezeles#amit-a-kassza-újrapróbál).
## Előellenőrzés az űrlapon [#előellenőrzés-az-űrlapon]
Mielőtt a szerveredet és a NAV-ot terhelnéd, ellenőrizd a beírt adószámot a böngészőben. A `kassza/validators` nem használ Agent kulcsot és hálózatot, ezért kliensoldalon is futhat.
| Függvény | Mit ellenőriz? |
| --- | --- |
| `parseHungarianTaxNumber(value)` | 11 jegyű adószám: a törzsszám ellenőrző számjegye, az áfakód (1–5) és a megyekód. Érvényes bemenetre `{ taxpayerId, vatCode, countyCode, formatted }`, egyébként `undefined`. |
| `isValidHungarianTaxNumber(value)` | Ugyanez, `boolean` eredménnyel. |
| `isValidHungarianTaxpayerId(value)` | Csak a 8 jegyű törzsszám és az ellenőrző számjegye. |
A validátorok a szóközöket és a kötőjeleket elhagyják, a `HU` előtagot viszont nem. Ha az űrlapon közösségi adószámot is elfogadsz, az előtagot ellenőrzés előtt vágd le.
```ts
import { parseHungarianTaxNumber } from 'kassza/validators'
const adoszam = parseHungarianTaxNumber(bevitt)
if (!adoszam) throw new Error('Érvénytelen adószám')
const ceg = await kassza.taxpayer.query(adoszam.formatted)
```
Próbáld ki:
A teljes folyamatot, az űrlaptól a számla vevőjéig, a [Válasz](/docs/adoszam-lekerdezes/valasz#a-vevő-kitöltése) oldal mutatja. A többi validátort a [Validátorok](/docs/kiegeszitok/validatorok) oldal sorolja fel.
# Válasz
URL: https://www.kasszajs.hu/docs/adoszam-lekerdezes/valasz
> A TaxpayerInfo és a TaxpayerAddress mezői, a valid false eset, a számla vevőjének kitöltése a válaszból, és az adószám lekérdezésnél előforduló hibák.
A `query()` egy `TaxpayerInfo` objektumot ad vissza. A mezők a NAV Online Számla `QueryTaxpayerResponse` válaszából jönnek, amelyet a Számlázz.hu változatlanul továbbít.
```ts
const ceg = await kassza.taxpayer.query('12345676-2-41')
if (ceg.valid && ceg.address) {
console.log(ceg.name, ceg.taxNumber?.formatted, ceg.address.formatted)
}
```
## TaxpayerInfo [#taxpayerinfo]
| Mező | Típus | Forrás a válaszban | Leírás |
| --- | --- | --- | --- |
| `valid` | `boolean` | `taxpayerValidity` | Érvényes-e az adószám. `false` esetén csak az `addresses`, az `infoDate` és a `requestId` szerepel. |
| `name` | `string \| undefined` | `taxpayerName` | Az adózó teljes neve. A kassza nem alakítja át, ezért jellemzően csupa nagybetű. |
| `shortName` | `string \| undefined` | `taxpayerShortName` | Rövid név, ha a NAV-nál van ilyen. Ez sincs átalakítva. |
| `taxNumber` | `TaxpayerTaxNumber \| undefined` | `taxNumberDetail` | Az adószám részei, lásd lent. |
| `address` | `TaxpayerAddress \| undefined` | `taxpayerAddressList` | A székhely (`HQ`), ennek hiányában az első cím. |
| `addresses` | `readonly TaxpayerAddress[]` | `taxpayerAddressList` | Az összes cím a válasz sorrendjében. Üres tömb, ha nincs cím. |
| `incorporation` | `TaxpayerIncorporation \| undefined` | `incorporation` | Az adózó típusa: `'ORGANIZATION'` (gazdálkodó szervezet), `'SELF_EMPLOYED'` (egyéni vállalkozó) vagy `'TAXABLE_PERSON'` (adószámos magánszemély). Más értéket változatlanul továbbad. |
| `vatGroupMembership` | `string \| undefined` | `vatGroupMembership` | Áfacsoport tagjánál a csoport azonosítója. |
| `infoDate` | `string \| undefined` | `infoDate` | A NAV adatainak dátuma ISO 8601 szövegként. |
| `requestId` | `string \| undefined` | `header/requestId` | A NAV kérés azonosítója. `-` érték esetén `undefined`. |
Ha a NAV érvényesnek jelöli az adószámot, de adatot nem küld, a `valid` `true`, az `addresses`
pedig üres. A vevő kitöltése előtt ezért a `valid` mellett az `address` meglétét is nézd meg.
### TaxpayerTaxNumber [#taxpayertaxnumber]
| Mező | Típus | Forrás | Leírás |
| --- | --- | --- | --- |
| `taxpayerId` | `string` | `taxpayerId` | A 8 jegyű törzsszám. Ha a válaszban nincs, a teljes `taxNumber` `undefined`. |
| `vatCode` | `string \| undefined` | `vatCode` | Az áfakód, az adószám 9. számjegye. |
| `countyCode` | `string \| undefined` | `countyCode` | A megyekód, az adószám utolsó két számjegye. |
| `formatted` | `string \| undefined` | kassza | `'12345676-2-41'` alakú adószám. Csak akkor van, ha a `vatCode` és a `countyCode` is megjött. |
## TaxpayerAddress [#taxpayeraddress]
| Mező | Típus | Forrás | Leírás |
| --- | --- | --- | --- |
| `type` | `TaxpayerAddressType` | `taxpayerAddressType` | `'HQ'` székhely, `'SITE'` telephely, `'BRANCH'` fióktelep. Nagybetűsítve, hiányzó típusnál `''`. |
| `countryCode` | `string` | `countryCode` | Országkód nagybetűvel, például `'HU'`. Hiányzó értéknél `''`. |
| `region` | `string \| undefined` | `region` | Megye vagy régió, olvasható formában. |
| `postalCode` | `string` | `postalCode` | Irányítószám. Hiányzó értéknél `''`. |
| `city` | `string` | `city` | Település, olvasható formában. Hiányzó értéknél `''`. |
| `street` | `string \| undefined` | `streetName` | A közterület neve, olvasható formában. |
| `publicPlaceCategory` | `string \| undefined` | `publicPlaceCategory` | A közterület jellege. Csupa nagybetűs értéknél kisbetűvel, például `'utca'`. |
| `number` | `string \| undefined` | `number` | Házszám. |
| `building` | `string \| undefined` | `building` | Épület. |
| `staircase` | `string \| undefined` | `staircase` | Lépcsőház. |
| `floor` | `string \| undefined` | `floor` | Emelet. |
| `door` | `string \| undefined` | `door` | Ajtó. |
| `lotNumber` | `string \| undefined` | `lotNumber` | Helyrajzi szám. |
| `formatted` | `string` | kassza | Egysoros cím, például `'1031 Budapest, Záhony utca 7.'`. |
| `raw` | `NavDetailedAddress` | `taxpayerAddress` | A NAV eredeti értékei átalakítás nélkül. |
A NAV a címeket jellemzően csupa nagybetűvel küldi. Az olvasható formában a kassza szavanként nagy kezdőbetűt ír, a kötőjeles neveket részenként alakítja, a római számot meghagyja: `BUDAPEST XI.` → `Budapest XI.`, `BAJCSY-ZSILINSZKY` → `Bajcsy-Zsilinszky`. A vegyes kis- és nagybetűs értékekhez nem nyúl. A `raw` mezőben (`countryCode`, `region`, `postalCode`, `city`, `streetName`, `publicPlaceCategory`, `number`, `building`, `staircase`, `floor`, `door`, `lotNumber`) az eredeti értékeket találod.
A `formatted` sorrendje: irányítószám és település, vessző, közterület, házszám, épület, lépcsőház, emelet, ajtó, vessző, helyrajzi szám, vessző, és a végén az országkód, ha nem `HU`. A csak számjegyből álló házszám, lépcsőház, emelet és ajtó után pontot tesz:
```text
1111 Budapest XI., Bajcsy-Zsilinszky út 12. B ép. 2. lh. 3. em. 14. ajtó
2000 Szentendre, hrsz. 1234/5
```
## Ha a valid false [#ha-a-valid-false]
Ha a NAV szerint a törzsszámhoz nem tartozik érvényes adószám, a `query()` nem dob hibát, hanem ezt adja vissza:
```ts
{ valid: false, addresses: [], infoDate: undefined, requestId: '38046_g2z6726bg67ymdt3p56bg6' }
```
Ilyenkor:
- ne töltsd ki a vevő adatait, és jelezd a felhasználónak, hogy ellenőrizze a beírt számot;
- ne kérdezd le újra ugyanazt a számot, ez nem átmeneti hiba, hanem a NAV válasza;
- ha később vissza kell nézned, mit válaszolt a NAV, naplózd a `requestId` mezőt.
## A vevő kitöltése [#a-vevő-kitöltése]
Az űrlapon beírt adószámot a böngésző előbb a `kassza/validators` modullal ellenőrzi, és csak érvényes formátumnál hívja a szervert. A szerver újra ellenőriz, lekérdezi a NAV-ot, és a válaszból összerakja a számla `buyer` mezőjét.
```tsx title="app/rendeles/adoszam-mezo.tsx"
'use client'
import type { InvoiceBuyer } from 'kassza'
import { parseHungarianTaxNumber } from 'kassza/validators'
import { useState } from 'react'
import { vevoAdoszambol } from './actions'
export function AdoszamMezo({ onVevo }: { onVevo: (vevo: InvoiceBuyer) => void }) {
const [hiba, setHiba] = useState()
async function kitolt(bevitt: string) {
const adoszam = parseHungarianTaxNumber(bevitt)
if (!adoszam) {
setHiba('Hibás adószám. Így add meg: 12345676-2-41')
return
}
const eredmeny = await vevoAdoszambol(adoszam.formatted)
if (!eredmeny.ok) {
setHiba(eredmeny.uzenet)
return
}
setHiba(undefined)
onVevo(eredmeny.vevo)
}
return (
)
}
```
```ts title="app/rendeles/actions.ts"
'use server'
import { type InvoiceBuyer, isSzamlazzError } from 'kassza'
import { parseHungarianTaxNumber } from 'kassza/validators'
import { kassza } from '@/lib/kassza'
type VevoEredmeny = { ok: true; vevo: InvoiceBuyer } | { ok: false; uzenet: string }
export async function vevoAdoszambol(bevitt: string): Promise {
const adoszam = parseHungarianTaxNumber(bevitt)
if (!adoszam) return { ok: false, uzenet: 'Hibás adószám.' }
try {
const ceg = await kassza.taxpayer.query(adoszam.formatted)
if (!ceg.valid || !ceg.address) {
return { ok: false, uzenet: 'A NAV nem talált érvényes adószámot. Ellenőrizd a számot.' }
}
return {
ok: true,
vevo: {
name: ceg.name ?? '',
zip: ceg.address.postalCode,
city: ceg.address.city,
address: ceg.address.formatted.split(', ').slice(1).join(', '),
taxNumber: ceg.taxNumber?.formatted ?? adoszam.formatted,
taxpayerType: 'hungarianTaxNumber',
},
}
} catch (error) {
if (isSzamlazzError(error) && error.retryable) {
return { ok: false, uzenet: 'A NAV most nem elérhető. Add meg kézzel a vevő adatait.' }
}
throw error
}
}
```
- Az `address` a `formatted` első vessző utáni része, mert az irányítószám és a település külön mezőbe kerül.
- A `taxNumber` a NAV válaszából jön, de ha abból hiányzik a megyekód, a beírt és ellenőrzött adószámot használja.
- A `retryable` hibákat (`maintenance`, `network`, `timeout`) a kassza már újrapróbálta, mire a `catch` ágba érsz, ezért itt nem kell újra hívnod.
A mezőket a [Számla létrehozás kérés](/docs/szamla-letrehozas/keres) oldala írja le.
## Hibák [#hibák]
| Kód | Kategória | Mikor fordul elő? |
| --- | --- | --- |
| – | `validation` | A bemenet a `HU` előtag, a szóközök és a kötőjelek nélkül nem 8 vagy 11 számjegy. A kassza kérést sem küld. |
| 3 | `auth` | Hibás Agent kulcs. |
| 136 | `account` | Lejárt előfizetés vagy rendezetlen díj. |
| 57 | `validation` | A kérés XML-je nem felel meg a sémának. A kassza formátum-ellenőrzése mellett nem jellemző. |
| – | `unknown` | A NAV szöveges hibakóddal válaszolt, például `INVALID_SECURITY_USER`. A kód az üzenet elején áll. |
| 1 | `maintenance` | Karbantartás. A kassza magától újrapróbálja. |
| – | `network`, `timeout` | Hálózati hiba, 5xx válasz vagy időtúllépés. A kassza újrapróbálja, és csak az utolsó próbálkozás hibáját kapod meg. |
| – | `unexpected_response` | A válasz nem `QueryTaxpayerResponse` XML. |
A NAV hibáját a válasz `result` eleme jelzi (`funcCode` értéke `ERROR`). Számjegyes `errorCode` esetén a kassza a Számlázz.hu hibakód táblája szerint adja a kategóriát. Az ott nem szereplő és a szöveges kódok `unknown` kategóriát kapnak. Az összes kódot a [Hibakezelés, hibakódok](/docs/alapok/hibakezeles#hibakódok) oldal sorolja fel.
Tesztekben a [mock kliens](/docs/kiegeszitok/teszteles) `taxpayers` opciójával adhatsz meg törzsszám szerinti válaszokat. Az ismeretlen törzsszámra a mock `{ valid: false, addresses: [] }` választ ad.
# Minta
URL: https://www.kasszajs.hu/docs/adoszam-lekerdezes/minta
> Adószám előellenőrzése, lekérdezése és a számla vevőjének kitöltése a sandboxban, a ténylegesen elküldött xmltaxpayer XML-lel és a NAV válaszával.
A példa a `kassza/validators` modullal ellenőrzi a beírt adószámot, lekérdezi a NAV-tól, és a válaszból összerakott vevővel számlát állít ki. Utána egy ismeretlen törzsszámot is lekérdez, amelyre `valid: false` jön. Az **Elküldött XML** fülön mindkét `xmltaxpayer` kérést és a számla kérését is látod, a **Válasz** fülön a `QueryTaxpayerResponse` XML-t.
## Kitalált adózók a sandboxban [#kitalált-adózók-a-sandboxban]
A sandbox nem hívja a NAV-ot. Ezt a három kitalált törzsszámot ismeri, minden más 8 jegyű törzsszámra `valid: false` választ ad:
| Adószám | `shortName` | `incorporation` |
| --- | --- | --- |
| `12345676-2-41` | `MINTA KERESKEDELMI KFT.` | `'ORGANIZATION'` |
| `23456787-2-13` | `MINTA WEBÁRUHÁZ KFT.` | `'ORGANIZATION'` |
| `11111111-1-06` | `TESZT ELEK E.V.` | `'SELF_EMPLOYED'` |
Mindhárom átmegy a `parseHungarianTaxNumber` ellenőrzésén, így a [sandboxban](/sandbox?pelda=adoszam) a bevitt számot bármelyikre átírhatod.
## XSD séma [#xsd-séma]
A kérés sémája:
```text
http://www.szamlazz.hu/docs/xsds/agent/xmltaxpayer.xsd
```
A válasz a NAV Online Számla `QueryTaxpayerResponse` szerkezetét követi, ennek sémáját a NAV Online Számla interfész dokumentációja tartalmazza.
# Fizetésből bizonylat
URL: https://www.kasszajs.hu/docs/fizetesek
> Stripe, SimplePay, Barion, Revolut és PayPal fizetésből automatikusan nyugta vagy számla, nulla függőséggel. Aláírás-ellenőrzés, a nyugta vagy számla döntése, pontosan egyszeri kiállítás és sztornó visszatérítéskor.
A webshop legtöbb számlázási hibája a fizetési webhookban születik: dupla számla újraküldött eseményből, rossz kerekítés, elfelejtett sztornó visszatérítéskor. A `kassza/payments` modul ezt egy helyen oldja meg. A fizetési szolgáltató webhookját ellenőrzi és egységes `PaymentEvent` objektummá alakítja, az `issueForPayment()` pedig ebből pontosan egyszer állít ki nyugtát vagy számlát.
```ts title="app/api/stripe/webhook/route.ts"
import { stripeWebhook } from 'kassza/payments/stripe'
import { kassza } from '@/lib/kassza'
export const POST = stripeWebhook({
secret: process.env.STRIPE_WEBHOOK_SECRET!,
apiKey: process.env.STRIPE_SECRET_KEY,
onPayment: (payment) => kassza.issueForPayment(payment, { vat: 27 }),
})
```
Nem kell hozzá a szolgáltató SDK-ja: az aláírást Web Cryptóval ellenőrzi, a szolgáltató API-ját `fetch`-csel hívja, így Node.js-ben, Cloudflare Workersben és Vercel Edge-en is fut.
## Szolgáltatók [#szolgáltatók]
| Szolgáltató | Import | Hitelesség | Részletek |
| --- | --- | --- | --- |
| Stripe | `kassza/payments/stripe` | HMAC-SHA256 aláírás | [Stripe](/docs/fizetesek/stripe) |
| SimplePay | `kassza/payments/simplepay` | HMAC-SHA384 aláírás, aláírt IPN-válasz | [SimplePay](/docs/fizetesek/simplepay) |
| Barion | `kassza/payments/barion` | Az állapotot a kassza a Barion API-tól kérdezi le | [Barion](/docs/fizetesek/barion) |
| Revolut | `kassza/payments/revolut` | HMAC-SHA256 aláírás és időbélyeg | [Revolut](/docs/fizetesek/revolut) |
| PayPal | `kassza/payments/paypal` | Ellenőrzés a PayPal API-val | [PayPal](/docs/fizetesek/paypal) |
Minden webhookkezelő `(request: Request) => Promise` függvény, így közvetlenül exportálható Next.js route handlerként, vagy meghívható Hono, Express (fetch adapterrel) és Workers alatt.
## A webhook válaszai [#a-webhook-válaszai]
| Helyzet | Válasz | Következmény |
| --- | --- | --- |
| Hibás aláírás, hiányzó vagy hibás adat | `400` | A szolgáltató hibát jelez; az esemény nem ér el az `onPayment`-ig. |
| Sikeres feldolgozás vagy olyan esemény, amelyhez nincs teendő | `200` (Revolutnál `204`, SimplePaynél aláírt JSON) | A szolgáltató nem küldi újra. |
| Az `onPayment` vagy a szolgáltató API-hívása hibát dobott | `500` | A szolgáltató később újraküldi az eseményt. |
Az `500`-as hibát az `onError` opcióban kapod meg naplózásra, alapból a `console.error` írja ki. Az újraküldés biztonságos, mert az `issueForPayment()` minden hívás előtt megkeresi a már kiállított bizonylatot.
## PaymentEvent [#paymentevent]
A webhookkezelő minden szolgáltató eseményét ugyanarra az alakra hozza:
| Mező | Jelentés |
| --- | --- |
| `provider` | `stripe`, `simplepay`, `barion`, `revolut` vagy `paypal` |
| `kind` | `paid`, `refunded` (teljes visszatérítés), `partially-refunded`, `failed` vagy `other` |
| `id` | A fizetés azonosítója a szolgáltatónál; a visszatérítés eseményében is ugyanaz |
| `orderRef` | A saját rendelésazonosítód, ha a szolgáltató visszaküldi |
| `amount`, `refundedAmount` | Összeg és pénznem, főegységben (`12700` Ft, nem fillérben) |
| `method` | A bizonylatra kerülő fizetési mód, például `bankkártya` |
| `customer` | Név, e-mail, cím, adószám, ha a szolgáltató tudja |
| `items` | Tételek bruttó összeggel, ha a szolgáltató tudja |
| `raw` | Az eredeti esemény |
## issueForPayment [#issueforpayment]
Az `issueForPayment()` a fizetés fajtája szerint dolgozik:
| `kind` | Mit csinál |
| --- | --- |
| `paid` | Eldönti, nyugta vagy számla jár, és pontosan egyszer kiállítja. |
| `refunded` | Sztornózza a fizetéshez kiállított számlát vagy nyugtát (pontosan egyszer). |
| `partially-refunded` | Számlánál visszatérítésenként egy helyesbítő számlát állít ki (pontosan egyszer); nyugtánál javaslatot ad, lásd [Részleges visszatérítés](#részleges-visszatérítés). |
| `failed`, `other` | Kihagyja. |
```ts
const eredmeny = await kassza.issueForPayment(payment, { vat: 27 })
switch (eredmeny.kind) {
case 'receipt':
case 'invoice':
console.log('Kiállítva:', eredmeny.number, eredmeny.created)
break
case 'reversal':
console.log('Sztornózva:', eredmeny.reversedNumber, eredmeny.number)
break
case 'correction':
console.log('Helyesbítve:', eredmeny.corrections.map((helyesbites) => helyesbites.number))
break
case 'refund-proposal':
console.log('Kézi teendő:', eredmeny.reason, eredmeny.refunds)
break
case 'skipped':
console.log('Nincs bizonylat:', eredmeny.reason)
}
```
A nyugta előtagját és fizetési módját a kliens `defaults.receipt` beállítása adja, a számláét a `defaults.invoice` (lásd [Közös kliens](/docs/receptek/kozos-kliens)). Egy-egy mezőt a `receipt` és az `invoice` opcióban írhatsz felül, például `receipt: { comment: 'Köszönjük!' }`. A nyugta előtagja nem lehet olyan, amelyet számlán már használtál (336).
### Nyugta vagy számla [#nyugta-vagy-számla]
Alapból a [nyugta vagy számla](/docs/alapok/nyugta-vagy-szamla) szabályai döntenek: számla jár, ha a vevő cég (adószámot adott meg), ha az összeg eléri a 900 000 Ft-ot, vagy ha a vevő számlát kér. Különben nyugta. A döntés indoklása az eredmény `decision` mezőjében van.
| Opció | Jelentés |
| --- | --- |
| `document` | `auto` (alapérték), `receipt` vagy `invoice` a döntés felülírására |
| `buyerIsBusiness` | A vevő cég, akkor is, ha nem adott meg adószámot |
| `invoiceRequested` | A vevő számlát kért |
| `cashRegisterRequired` | A tevékenységed pénztárgép-köteles: nyugta helyett hibát kapsz, mert Agent-nyugta nem adható |
| `buyer` | A számla vevője; alapból a fizetés vevőadataiból készül, ha van név és teljes cím |
### Tételek és áfa [#tételek-és-áfa]
A tételek sorrendje: az `items` opció, a szolgáltatótól kapott tételek, végül egyetlen tétel a kifizetett összeggel (`fallbackItemName`, alapból „Termék vagy szolgáltatás”). Az áfakulcs a `vatFor(tétel)` függvényből, a szolgáltató tételenkénti áfájából vagy a `vat` opcióból jön, ebben a sorrendben. Ha egyik sincs, `validation` hibát kapsz: az áfát a kassza soha nem találja ki.
A kassza ellenőrzi, hogy a bizonylat bruttó végösszege egyezik-e a kifizetett összeggel. Ha nem (például kimaradt a szállítási díj), `validation` hibát ad. Szándékos eltérésnél add meg az `allowAmountMismatch: true` opciót.
### Deviza [#deviza]
A bizonylat pénzneme mindig a fizetésé. Devizás nyugtához add meg az árfolyamot (`exchangeRate`); a bank alapból az MNB. Devizás számlánál árfolyam nélkül az MNB napi árfolyamát használja a Számlázz.hu.
### Pontosan egyszer [#pontosan-egyszer]
A rendelésszám a szolgáltatóból és a fizetés azonosítójából áll, például `STRIPE-pi_3Q…`, így a fizetés és a későbbi visszatérítés ugyanahhoz a rendelésszámhoz kötődik. Saját rendelésszámot az `orderNumber` opcióval adhatsz meg.
- **Nyugta:** `receipts.createOnce()` a rendelésszámmal, hívásazonosítóként is a rendelésszámot küldve.
- **Számla:** `invoices.createOnce()`, külső azonosítónak a rendelésszámmal, kifizetettként.
- **Sztornó:** számlánál `{rendelésszám}/SS` külső azonosítóval, nyugtánál `{rendelésszám}/SN` hívásazonosítóval.
Ha a webhook kétszer érkezik, a második hívás `created: false` eredményt ad, és nem állít ki új bizonylatot. Ha a sztornó után ismét `paid` esemény jön, a kassza kihagyja, mert a rendeléshez tartozó nyugtát már sztornózták. Ha a nyugtát időközben [számlává alakították](/docs/alapok/nyugta-vagy-szamla#ha-a-vevő-utólag-számlát-kér), visszatérítéskor az átalakított számlát sztornózza.
### Részleges visszatérítés [#részleges-visszatérítés]
Ha a vásárló a rendelés egy részét kapja vissza, a szolgáltatók `partially-refunded` eseményt küldenek. A kassza ilyenkor az eredeti számlához **visszatérítésenként egy helyesbítő számlát** állít ki, negatív mennyiségű tételekkel:
- A külső azonosító `{rendelésszám}/R-{visszatérítés}`, így egy újraküldött webhook vagy egy több visszatérítést felsoroló esemény sem állít ki duplát.
- A visszatérített összeget a kassza az eredeti tételek között **pontosan** osztja szét, tételenként a saját áfakulcsán. Több egymás utáni visszatérítés együtt sem haladja meg egyik tétel eredeti összegét sem, és a teljes összeg visszatérítése pontosan az eredeti tételeket adja.
- A szolgáltatók tételesen adják a visszatérítéseket (`payment.refunds`): Barionnál és SimplePaynél a teljes listát, Revolutnál és PayPalnál az aktuálisat, Stripe-nál az esemény azonosítójával és a `previous_attributes` különbségével. Ha ez hiányzik, a kassza kihagyja az eseményt (`skipped`), és megírja, miért.
**Ha az eredeti számlán több áfakulcs van**, a kassza nem tudhatja, melyik tétel került vissza (egy 5%-os könyv és egy 27%-os póló közül nem mindegy, melyiket hozták vissza). Ilyenkor alapból nem állít ki semmit, hanem `refund-proposal` eredményt ad a javasolt bontással. Két lehetőséged van:
```ts
await kassza.issueForPayment(payment, {
refundItems: ({ refund, items }) => visszakuldottTetelek(refund.id),
})
await kassza.issueForPayment(payment, { partialRefund: 'proportional' })
```
A `refundItems` a visszatérített tételeket adja vissza pozitív bruttó egységárral (a kassza fordítja negatívba, és ellenőrzi, hogy az összeg egyezik). A `partialRefund: 'proportional'` arányos szétosztást kér, ami például százalékos utólagos kedvezménynél helyes. A `partialRefund: 'skip'` a régi viselkedést adja vissza (kihagyás).
**Nyugtánál** a részleges visszatérítés jogi menete nem egyértelmű (sztornó és új nyugta a megmaradó tételekről), ezért a kassza nem állít ki semmit: `refund-proposal` eredményt ad a kiszámolt bontással, amit a könyvelőddel egyeztetve kézzel intézhetsz.
### Ismételt webhookok szűrése [#ismételt-webhookok-szűrése]
A szolgáltatók ugyanazt az eseményt napokig újraküldhetik. A `createOnce` ilyenkor sem állít ki duplát, de minden újraküldés legalább egy lekérdezés a Számla Agent felé. A `dedupe` opcióval a már **sikeresen** feldolgozott eseményt a kassza a Számla Agent hívása nélkül nyugtázza:
```ts
import { upstashRedisStore } from 'kassza/stores'
export const POST = stripeWebhook({
secret: process.env.STRIPE_WEBHOOK_SECRET ?? '',
dedupe: upstashRedisStore(Redis.fromEnv()),
onPayment: (payment) => kassza.issueForPayment(payment, { vat: 27 }),
})
```
Az eseményt csak a sikeres `onPayment` után jelöli feldolgozottnak (alapból 7 napig, `dedupeTtlSeconds`), így egy elbukott feldolgozás újraküldve újra lefut. Ha a tároló nem érhető el, a feldolgozás megtörténik, és figyelmeztetés megy az `onWarning`-ra.
## Hibakezelés a webhookban [#hibakezelés-a-webhookban]
Ha az `issueForPayment()` hibát dob, a webhook `500`-at ad, és a szolgáltató később újraküldi. Ez hálózati hibánál és karbantartásnál pont jó, de egy hibás adatból (például hiányzó áfakulcs vagy számlacím) eredő hiba újraküldve is hibás marad. Ezeket érdemes elkapni és emberre bízni:
```ts title="app/api/stripe/webhook/route.ts"
import { isSzamlazzError } from 'kassza'
import { stripeWebhook } from 'kassza/payments/stripe'
import { kassza } from '@/lib/kassza'
import { kezelesreVar } from '@/lib/rendelesek'
export const POST = stripeWebhook({
secret: process.env.STRIPE_WEBHOOK_SECRET!,
apiKey: process.env.STRIPE_SECRET_KEY,
onPayment: async (payment) => {
try {
await kassza.issueForPayment(payment, { vat: 27 })
} catch (error) {
if (isSzamlazzError(error) && !error.retryable) {
await kezelesreVar(payment.id, `${error.category}: ${error.message}`)
return
}
throw error
}
},
})
```
A szolgáltatók napokig újraküldhetik az eseményt. A Számlázz.hu egy kérést legfeljebb ötször
enged elküldeni, ezért több szerver vagy serverless példány esetén állítsd be a kliensen az
`attemptLedger` opciót, lásd [Pontosan egyszer](/docs/alapok/pontosan-egyszer#kitiltás-elleni-védelem-folyamatok-között).
## Más fizetési szolgáltató [#más-fizetési-szolgáltató]
Ha a szolgáltatód nincs a listán (például egy kártyaterminál webhookja), a `PaymentEvent` objektumot magad is összeállíthatod, a `respondToWebhook()` pedig ugyanazt a hibakezelést adja, mint a beépített kezelők: `WebhookVerificationError` esetén `400`, más hibánál `500`.
```ts title="app/api/terminal/webhook/route.ts"
import { type PaymentEvent, respondToWebhook, WebhookVerificationError } from 'kassza/payments'
import { kassza } from '@/lib/kassza'
import { terminalAlairasJo } from '@/lib/terminal'
export function POST(request: Request) {
return respondToWebhook({}, async () => {
const body = await request.text()
if (!(await terminalAlairasJo(request, body))) {
throw new WebhookVerificationError('terminal', 'invalid_signature', 'Hibás aláírás.')
}
const esemeny = JSON.parse(body) as { id: string; osszeg: number; statusz: string }
const payment: PaymentEvent = {
provider: 'terminal',
kind: esemeny.statusz === 'sikeres' ? 'paid' : 'other',
id: esemeny.id,
amount: { value: esemeny.osszeg, currency: 'HUF' },
method: 'bankkártya',
raw: esemeny,
}
await kassza.issueForPayment(payment, { vat: 27 })
return new Response('OK')
})
}
```
A `provider` tetszőleges címke lehet; a rendelésszám ebből és az azonosítóból készül (itt `TERMINAL-{id}`).
## Kapcsolódó [#kapcsolódó]
A négy feltétel, a pénztárgép-kötelezettség és a chooseDocument.
createOnce, kitiltás elleni védelem és onDocument hook.
A webhook kódod tesztelése elveszett válasszal és karbantartással.
# Stripe
URL: https://www.kasszajs.hu/docs/fizetesek/stripe
> Stripe Checkout és Payment Intent fizetésből nyugta vagy számla, visszatérítésből sztornó a kassza/payments/stripe webhookkezelővel, Stripe SDK nélkül.
A `stripeWebhook()` ellenőrzi a `Stripe-Signature` fejlécet, a Stripe eseményéből `PaymentEvent`-et készít, és meghívja az `onPayment` függvényedet. A Stripe SDK nem kell hozzá.
## Beállítás [#beállítás]
1. A Stripe Dashboardon a **Developers / Webhooks** alatt vegyél fel egy végpontot, például `https://bolt.example.hu/api/stripe/webhook`.
2. Válaszd ki az eseményeket: `checkout.session.completed`, `checkout.session.async_payment_succeeded`, `checkout.session.async_payment_failed` és `charge.refunded`. Checkout nélkül (saját Payment Element) a `payment_intent.succeeded` és a `payment_intent.payment_failed` kell a Checkout események helyett.
3. Másold ki a végpont aláíró titkát (`whsec_…`).
```dotenv title=".env.local"
STRIPE_WEBHOOK_SECRET=whsec_...
STRIPE_SECRET_KEY=rk_live_...
```
Az API-kulcs nem kötelező, de nélküle a bizonylat egyetlen tételből áll a kifizetett összeggel. Elég egy korlátozott (restricted) kulcs, amely olvashatja a Checkout Sessionöket és a Refundokat.
## A webhook [#a-webhook]
```ts title="app/api/stripe/webhook/route.ts"
import { stripeWebhook } from 'kassza/payments/stripe'
import { kassza } from '@/lib/kassza'
export const POST = stripeWebhook({
secret: process.env.STRIPE_WEBHOOK_SECRET!,
apiKey: process.env.STRIPE_SECRET_KEY,
onPayment: (payment) => kassza.issueForPayment(payment, { vat: 27 }),
})
```
| Opció | Alapérték | Jelentés |
| --- | --- | --- |
| `secret` | – | Az aláíró titok. Kulcsváltáskor tömbként a régit és az újat is megadhatod. |
| `apiKey` | – | Titkos vagy korlátozott API-kulcs a tételek és a visszatérítések lekéréséhez. |
| `toleranceSeconds` | `300` | Ennél régebbi aláírást elutasít (visszajátszás elleni védelem). |
| `method` | `bankkártya` | A bizonylatra kerülő fizetési mód. |
| `shippingItemName` | `Szállítási díj` | A szállítási díj tételének neve. |
| `onPayment` | – | A feldolgozó függvényed. |
| `onError` | `console.error` | Az `500`-as válaszhoz vezető hibák naplózása. |
| `maxBodyBytes` | 1 MiB | A törzs legnagyobb mérete. |
## Események [#események]
| Stripe esemény | `kind` |
| --- | --- |
| `checkout.session.completed` fizetett állapotban | `paid` |
| `checkout.session.completed` fizetés nélküli munkamenetre | `other` |
| `checkout.session.async_payment_succeeded` | `paid` |
| `checkout.session.async_payment_failed` | `failed` |
| `payment_intent.succeeded` | `paid` |
| `payment_intent.payment_failed` | `failed` |
| `charge.refunded` | `refunded` vagy `partially-refunded` |
Más eseményre a kezelő `200`-at ad, és nem hívja meg az `onPayment`-et. A fizetés azonosítója mindig a Payment Intent azonosítója, így a `charge.refunded` ugyanahhoz a rendelésszámhoz tartozik, mint a fizetés.
## Mit tölt ki API-kulccsal? [#mit-tölt-ki-api-kulccsal]
- **Tételek:** a Checkout Session `line_items` listáját lapozva lekéri, a szállítási díjat külön tételként adja hozzá.
- **Payment Intent → Checkout:** a `payment_intent.succeeded` eseményhez megkeresi a hozzá tartozó Checkout Sessiont, és abból veszi a vevőt és a tételeket.
- **Visszatérítés:** ha egy fizetést több részletben térítettél vissza, a teljes összeg elérésekor sem sztornóz automatikusan, hanem `partially-refunded` eseményt ad. Ezt a könyvelőddel egyeztetve kell rendezni.
## Vevőadatok és céges számla [#vevőadatok-és-céges-számla]
A vevőt a Checkout `customer_details` mezőjéből veszi: név (cégnév, ha van), e-mail, cím, adószám. A magyar adószám (`hu_tin`) a `taxNumber`, az uniós adószám (`eu_vat`) az `euTaxNumber` mezőbe kerül, és bármelyik adószám esetén a vevőt cégnek tekinti, így számla készül. Kapcsold be a Checkoutban a számlázási cím (`billing_address_collection: 'required'`) és az adószám (`tax_id_collection`) bekérését.
A saját rendelésazonosítódat a `client_reference_id` mezőben vagy a `metadata` `orderRef`, `orderId` vagy `orderNumber` kulcsán add át; a `PaymentEvent` `orderRef` mezőjében kapod vissza.
A Stripe a forintot is két tizedesjegyű fillérben adja meg (`1270000` = 12 700 Ft). A kassza ezt
átváltja, a `PaymentEvent` összegei mindig főegységben vannak.
## Buktatók [#buktatók]
Az aláírás a nyers törzsre szól. Ha egy middleware előbb JSON-ként beolvassa a kérést, az
ellenőrzés elbukik. App Routerben a `stripeWebhook()` maga olvassa a törzset, ne nyúlj hozzá
előtte.
A Stripe teszt- és éles módban külön végpontot és titkot használ. A teszt módú végpont mellé
Számlázz.hu tesztfiók tartozzon, különben valódi bizonylatok készülnek.
## Kapcsolódó [#kapcsolódó]
Az issueForPayment döntései, tételek, áfa és hibakezelés.
Ugyanez a Stripe SDK-val és saját számlázó függvénnyel.
# SimplePay
URL: https://www.kasszajs.hu/docs/fizetesek/simplepay
> OTP SimplePay v2 IPN-ből nyugta vagy számla a kassza/payments/simplepay kezelővel. Aláírás-ellenőrzés, aláírt IPN-visszaigazolás, tranzakció-lekérdezés, több kereskedői fiók és a visszairányítás ellenőrzése.
A SimplePay minden lezárt fizetésről és visszatérítésről IPN üzenetet küld. A `simplePayWebhook()` ellenőrzi az aláírást (HMAC-SHA384), lekérdezi a tranzakció részleteit, meghívja az `onPayment` függvényedet, majd a SimplePay által elvárt aláírt visszaigazolással válaszol. Ha a visszaigazolás elmarad vagy hibás, a SimplePay újraküldi az IPN-t.
## Beállítás [#beállítás]
A SimplePay kereskedői felületén az IPN URL legyen a végpontod, például `https://bolt.example.hu/api/simplepay/ipn`. A titkos kulcs (SECRET_KEY) a kereskedői fiók adatai között van.
```dotenv title=".env.local"
SIMPLEPAY_SECRET_KEY=...
```
## A webhook [#a-webhook]
```ts title="app/api/simplepay/ipn/route.ts"
import { simplePayWebhook } from 'kassza/payments/simplepay'
import { kassza } from '@/lib/kassza'
import { rendelesTetelei } from '@/lib/rendelesek'
export const POST = simplePayWebhook({
secretKey: process.env.SIMPLEPAY_SECRET_KEY!,
onPayment: async (payment) => {
const items = payment.orderRef ? await rendelesTetelei(payment.orderRef) : undefined
await kassza.issueForPayment(payment, { vat: 27, items })
},
})
```
A SimplePay IPN-je nem tartalmaz tételeket. A recept ezért a saját rendelésedből tölti be őket a `orderRef` alapján. Tételek nélkül a bizonylat egyetlen tételből áll a kifizetett összeggel.
| Opció | Alapérték | Jelentés |
| --- | --- | --- |
| `secretKey` | – | A kereskedői fiók titkos kulcsa. |
| `merchants` | – | Több kereskedői fiók: `{ MERCHANT: 'kulcs' }` vagy `{ MERCHANT: { secretKey, method } }`. |
| `fetchDetails` | `true` | Lekérdezi a tranzakciót (összeg, pénznem, vevő, visszatérítések). |
| `sandbox` | `false` | A sandbox API-t hívja. |
| `method` | – | Fix fizetési mód. Alapból bankkártyás fizetésnél `bankkártya`, átutalásnál `átutalás`. |
| `onPayment`, `onError`, `maxBodyBytes` | – | Lásd [Fizetésből bizonylat](/docs/fizetesek#a-webhook-válaszai). |
## Állapotok [#állapotok]
| SimplePay állapot | `kind` |
| --- | --- |
| `FINISHED` | `paid` |
| `REFUND`, egyetlen teljes visszatérítés | `refunded` |
| `REFUND`, részleges vagy több részletben | `partially-refunded` |
| `NOTAUTHORIZED`, `FRAUD`, `TIMEOUT`, `CANCELLED`, `REVERSED` | `failed` |
| Egyéb | `other` |
A visszatérítés fajtáját a tranzakció lekérdezése alapján dönti el. Ha a lekérdezés nem adja vissza a tranzakciót, a kezelő `500`-at ad, és a SimplePay később újraküldi az IPN-t.
## Több kereskedői fiók [#több-kereskedői-fiók]
A SZÉP-kártyás fizetés például külön kereskedői fiókkal megy. A `merchants` opcióban fiókonként adhatod meg a kulcsot és a bizonylatra kerülő fizetési módot:
```ts
export const POST = simplePayWebhook({
merchants: {
[process.env.SIMPLEPAY_MERCHANT!]: process.env.SIMPLEPAY_SECRET_KEY!,
[process.env.SIMPLEPAY_SZEP_MERCHANT!]: {
secretKey: process.env.SIMPLEPAY_SZEP_SECRET_KEY!,
method: 'SZÉP kártya',
},
},
onPayment: (payment) => kassza.issueForPayment(payment, { vat: 27 }),
})
```
Ismeretlen kereskedő IPN-jére a kezelő `400`-at ad.
## A visszairányítás ellenőrzése [#a-visszairányítás-ellenőrzése]
A fizetés után a SimplePay a vevőt az `r` és `s` paraméterrel irányítja vissza az oldaladra. A `verifySimplePayRedirect()` ellenőrzi az aláírást, és visszaadja az eredményt. A bizonylatot ne itt állítsd ki: a visszairányítás elmaradhat, az IPN viszont megérkezik.
```ts title="app/fizetes/vissza/page.tsx"
import { verifySimplePayRedirect } from 'kassza/payments/simplepay'
export default async function Vissza({ searchParams }: { searchParams: Promise> }) {
const eredmeny = await verifySimplePayRedirect(
new URLSearchParams(await searchParams),
process.env.SIMPLEPAY_SECRET_KEY!,
)
return
{eredmeny.event === 'SUCCESS' ? 'Köszönjük a fizetést!' : 'A fizetés nem sikerült.'}
}
```
## Kapcsolódó [#kapcsolódó]
Az issueForPayment döntései, tételek, áfa és hibakezelés.
A másik nagy hazai fizetési szolgáltató.
# Barion
URL: https://www.kasszajs.hu/docs/fizetesek/barion
> Barion fizetésből nyugta vagy számla a kassza/payments/barion callback-kezelővel. A fizetés állapotát a kassza a Barion API-tól kérdezi le, így hamis callback nem állít ki bizonylatot.
A Barion callback csak a fizetés azonosítóját (`paymentId`) küldi, aláírás nélkül. A `barionWebhook()` ezért nem bízik a kérésben: a POSKey-jel lekérdezi a fizetés állapotát a Barion API-tól (`GET /v4/Payment/{paymentId}/PaymentState`), és csak a válasz alapján hívja meg az `onPayment` függvényedet.
## Beállítás [#beállítás]
A fizetés indításakor a `CallbackUrl` legyen a végpontod, például `https://bolt.example.hu/api/barion/callback`. A POSKey a Barion shop adatai között van.
```dotenv title=".env.local"
BARION_POS_KEY=...
```
## A webhook [#a-webhook]
```ts title="app/api/barion/callback/route.ts"
import { barionWebhook } from 'kassza/payments/barion'
import { kassza } from '@/lib/kassza'
export const POST = barionWebhook({
posKey: process.env.BARION_POS_KEY!,
onPayment: (payment) => kassza.issueForPayment(payment, { vat: 27 }),
})
```
| Opció | Alapérték | Jelentés |
| --- | --- | --- |
| `posKey` | – | A shop POSKey-e. |
| `sandbox` | `false` | A teszt Barion API-t hívja (`api.test.barion.com`). |
| `method` | – | Fix fizetési mód. Alapból kártyánál `bankkártya`, átutalásnál `átutalás`, egyébként `Barion`. |
| `onPayment`, `onError`, `maxBodyBytes` | – | Lásd [Fizetésből bizonylat](/docs/fizetesek#a-webhook-válaszai). |
A kezelő a `paymentId`-t a query stringből, űrlapként vagy JSON-ként küldött törzsből is kiolvassa. Ellenőrzi, hogy érvényes azonosító-e, és hogy a Barion ugyanarról a fizetésről adott-e választ.
## Állapotok [#állapotok]
| Barion állapot | `kind` |
| --- | --- |
| `Succeeded` | `paid` |
| Sikeres visszatérítés a teljes összegről, egy részletben | `refunded` |
| Részleges vagy több részletben történt visszatérítés | `partially-refunded` |
| `Canceled`, `Failed`, `Expired` | `failed` |
| Egyéb | `other` |
A tételeket a fizetés tranzakcióinak `Items` listájából veszi, a díj- és visszatérítési tranzakciókat kihagyja.
A Barion csak a fizető nevét és e-mail címét adja vissza, címet nem. Ha a fizető szervezet, a
kassza számlát állítana ki, ehhez viszont cím kell. Céges vevőnél add át a számla vevőjét a saját
rendelésedből a `buyer` opcióban.
## Kapcsolódó [#kapcsolódó]
Az issueForPayment döntései, tételek, áfa és hibakezelés.
Aláírt IPN és több kereskedői fiók.
# Revolut
URL: https://www.kasszajs.hu/docs/fizetesek/revolut
> Revolut Business (Merchant API) fizetésből nyugta vagy számla a kassza/payments/revolut webhookkezelővel. Aláírás- és időbélyeg-ellenőrzés, a rendelés lekérése, visszatérítésből sztornó.
A Revolut webhook csak az eseményt és a rendelés azonosítóját küldi. A `revolutWebhook()` ellenőrzi a `Revolut-Signature` aláírást és a `Revolut-Request-Timestamp` időbélyeget, majd a titkos API-kulccsal lekéri a rendelést, és abból készít `PaymentEvent`-et.
## Beállítás [#beállítás]
1. A Revolut Business felületén (**Merchant / API**) hozz létre webhookot a végpontodra, például `https://bolt.example.hu/api/revolut/webhook`, az `ORDER_COMPLETED`, `ORDER_CANCELLED`, `ORDER_FAILED`, `ORDER_PAYMENT_DECLINED` és `ORDER_PAYMENT_FAILED` eseményekre.
2. A webhook aláíró titka a webhook adatai között van; a titkos API-kulcs a Merchant API beállításoknál.
```dotenv title=".env.local"
REVOLUT_WEBHOOK_SECRET=wsk_...
REVOLUT_SECRET_KEY=sk_...
```
## A webhook [#a-webhook]
```ts title="app/api/revolut/webhook/route.ts"
import { revolutWebhook } from 'kassza/payments/revolut'
import { kassza } from '@/lib/kassza'
export const POST = revolutWebhook({
secret: process.env.REVOLUT_WEBHOOK_SECRET!,
apiKey: process.env.REVOLUT_SECRET_KEY!,
onPayment: (payment) => kassza.issueForPayment(payment, { vat: 27 }),
})
```
| Opció | Alapérték | Jelentés |
| --- | --- | --- |
| `secret` | – | Az aláíró titok. Kulcsváltáskor tömbként több is megadható. |
| `apiKey` | – | A titkos API-kulcs a rendelés lekéréséhez. Kötelező. |
| `sandbox` | `false` | A sandbox API-t hívja. |
| `toleranceMs` | 5 perc | Ennél régebbi időbélyeget elutasít. |
| `apiVersion` | `2026-08-17` | A `Revolut-Api-Version` fejléc. |
| `method` | – | Fix fizetési mód. Alapból kártyánál `bankkártya`, Revolut Pay-nél `Revolut Pay`, SEPA beszedésnél `csoportos beszedés`. |
| `onPayment`, `onError`, `maxBodyBytes` | – | Lásd [Fizetésből bizonylat](/docs/fizetesek#a-webhook-válaszai). |
Sikeres feldolgozás után a kezelő `204`-et ad.
## Események [#események]
| Revolut esemény | `kind` |
| --- | --- |
| `ORDER_COMPLETED` fizetési rendelésre | `paid` |
| `ORDER_COMPLETED` visszatérítési rendelésre | `refunded` vagy `partially-refunded` |
| `ORDER_CANCELLED`, `ORDER_FAILED`, `ORDER_PAYMENT_DECLINED`, `ORDER_PAYMENT_FAILED` | `failed` |
| `ORDER_AUTHORISED` | `other` |
A Revolut a visszatérítést külön rendelésként kezeli. A kassza ilyenkor az eredeti rendelést is lekéri, így a visszatérítés ugyanahhoz a rendelésszámhoz kötődik, mint a fizetés.
A tételeket a rendelés `line_items` listájából, a vevőt a `customer`, a számlázási és a szállítási cím mezőkből veszi. A saját rendelésazonosítód a `merchant_order_data.reference` mezőből a `PaymentEvent` `orderRef` mezőjébe kerül.
## Kapcsolódó [#kapcsolódó]
Az issueForPayment döntései, tételek, áfa és hibakezelés.
PayPal capture és visszatérítés.
# PayPal
URL: https://www.kasszajs.hu/docs/fizetesek/paypal
> PayPal capture-ből nyugta vagy számla, visszatérítésből sztornó a kassza/payments/paypal webhookkezelővel. Aláírás-ellenőrzés a PayPal API-val, tételek a rendelésből.
A `payPalWebhook()` a PayPal hivatalos ellenőrző végpontjával (`/v1/notifications/verify-webhook-signature`) vizsgálja meg az eseményt, majd lekéri a hozzá tartozó rendelést, és abból készít `PaymentEvent`-et. Az OAuth tokent a kassza kéri le és tárolja, lejáratkor magától megújítja.
## Beállítás [#beállítás]
1. A PayPal Developer Dashboardon (**Apps & Credentials**) az alkalmazásodhoz vegyél fel webhookot a végpontodra, például `https://bolt.example.hu/api/paypal/webhook`, a `PAYMENT.CAPTURE.COMPLETED`, `PAYMENT.CAPTURE.DENIED`, `PAYMENT.CAPTURE.DECLINED`, `PAYMENT.CAPTURE.PENDING` és `PAYMENT.CAPTURE.REFUNDED` eseményekre.
2. Másold ki a webhook azonosítóját (Webhook ID), az alkalmazás Client ID-ját és titkát.
```dotenv title=".env.local"
PAYPAL_WEBHOOK_ID=...
PAYPAL_CLIENT_ID=...
PAYPAL_CLIENT_SECRET=...
```
## A webhook [#a-webhook]
```ts title="app/api/paypal/webhook/route.ts"
import { payPalWebhook } from 'kassza/payments/paypal'
import { kassza } from '@/lib/kassza'
export const POST = payPalWebhook({
webhookId: process.env.PAYPAL_WEBHOOK_ID!,
clientId: process.env.PAYPAL_CLIENT_ID!,
clientSecret: process.env.PAYPAL_CLIENT_SECRET!,
onPayment: (payment) => kassza.issueForPayment(payment, { vat: 27 }),
})
```
| Opció | Alapérték | Jelentés |
| --- | --- | --- |
| `webhookId` | – | A webhook azonosítója az aláírás ellenőrzéséhez. |
| `clientId`, `clientSecret` | – | Az alkalmazás azonosítója és titka. |
| `sandbox` | `false` | A sandbox API-t hívja. |
| `fetchOrder` | `true` | Fizetésnél lekéri a rendelést a tételekért és a vevőért. |
| `method` | `PayPal` | A bizonylatra kerülő fizetési mód. |
| `onPayment`, `onError`, `maxBodyBytes` | – | Lásd [Fizetésből bizonylat](/docs/fizetesek#a-webhook-válaszai). |
## Események [#események]
| PayPal esemény | `kind` |
| --- | --- |
| `PAYMENT.CAPTURE.COMPLETED` | `paid` |
| `PAYMENT.CAPTURE.DENIED`, `PAYMENT.CAPTURE.DECLINED` | `failed` |
| `PAYMENT.CAPTURE.PENDING` | `other` |
| `PAYMENT.CAPTURE.REFUNDED`, a teljes összegről | `refunded` |
| `PAYMENT.CAPTURE.REFUNDED`, részleges | `partially-refunded` |
A fizetés azonosítója a capture azonosítója. A visszatérítést a kassza a capture-höz köti, így ugyanahhoz a rendelésszámhoz tartozik, mint a fizetés.
## Tételek [#tételek]
A tételeket a rendelés `purchase_units` mezőjéből veszi, tételenként az áfával együtt, a szállítási, kezelési és biztosítási díjat külön tételként. Ha a rendelésben kedvezmény van, vagy nem egyértelmű a felosztás (több purchase unit, eltérő pénznem, hiányzó tételadó), a tételeket kihagyja. Ilyenkor a bizonylat egyetlen tételből áll a kifizetett összeggel, vagy add át a tételeket a saját rendelésedből az `items` opcióban.
A saját rendelésazonosítódat a `custom_id` vagy az `invoice_id` mezőben add át; a `PaymentEvent` `orderRef` mezőjében kapod vissza.
## Kapcsolódó [#kapcsolódó]
Az issueForPayment döntései, tételek, áfa és hibakezelés.
Checkout Session és Payment Intent.
# NAV nyugta-adatszolgáltatás
URL: https://www.kasszajs.hu/docs/nav-nyugta
> 2026. szeptember 1-jétől a kézi és a számítógéppel előállított nyugtákról napi összesítőt kell küldeni a NAV-nak. Mit vállal ebből a Számlázz.hu, és miben segít a kassza.
2026. szeptember 1-jétől a **kézi és a számítógéppel előállított nyugtákról** is adatot kell szolgáltatni a NAV-nak (Áfa tv. 257/G. §). A Számla Agenttel kiállított nyugta számítógéppel előállított nyugta, tehát érinti a szabály. A NAV 2026. december 31-ig türelmi időt ad.
A türelmi idő végéig érdemes felkészülni. A [Mi a teendő december 31. előtt?](/docs/nav-nyugta/hatarido)
oldal döntési fát ad a lehetséges helyzetekre.
## Mit kell beküldeni? [#mit-kell-beküldeni]
- **Határidő:** a nyugta kibocsátását követő 3 naptári napon belül.
- **Formája:** napi összesítő tárgynaponként, nyugtasorozatonként (tömbönként) és pénznemenként.
- **Tartalma:** a tárgynap, a kezdő nyugtaszám, a pénznem (devizánál az árfolyam), és NAV-áfakategóriánként (`0%`, `5%`, `18%`, `27%`, `Alanyi adómentes`, `Egyéb`) az értékesítés és a módosító (sztornó) bizonylatok bruttó összege. Végül a végösszeg, valamint az eladások és a módosító bizonylatok száma **külön-külön**.
- **Csatornája:** kézzel a Pénztárgép Portálon (KOBAK), vagy gépi interfészen.
Az online pénztárgép és az e-pénztárgép nyugtáit nem kell így jelenteni, azok eleve valós időben mennek a NAV-hoz.
## Mit vállal a Számlázz.hu? [#mit-vállal-a-számlázzhu]
A Számlázz.hu a nála kiállított számítógépes nyugtákat (az Agenttel kiállítottakat is) a NAV összekötés után maga jelenti. Ehhez a fiókban be kell állítani a NAV Online adatkapcsolatot. Hogy a fiókodban mikortól él az automatikus beküldés, azt a Számlázz.hu tájékoztatásából követheted.
Ha a Számlázz.hu jelent helyetted, **ne küldd be te is ugyanazt a nyugtát**, mert az kettős adatszolgáltatás. A kassza NAV kliense ezért alapból csak olvas.
## Miben segít a kassza? [#miben-segít-a-kassza]
| Feladat | Eszköz |
| --- | --- |
| Napi összesítő a kiállított nyugtákból, a NAV szabályai szerint | [`navDailyReports()`](/docs/nav-nyugta/napi-osszesito) a `kassza/reports` modulban |
| Pénztárzárás fizetési módonként és áfakulcsonként | [`dailyClose()`](/docs/nav-nyugta/napi-osszesito#napi-pénztárzárás) |
| A NAV-nál rögzített adatszolgáltatások lekérdezése és szükség esetén beküldése | [NAV kliens](/docs/nav-nyugta/nav-kliens) a `kassza/nav` modulban |
| Annak ellenőrzése, hogy minden nap rendben beérkezett-e a NAV-hoz | [Egyeztetés](/docs/nav-nyugta/egyeztetes) |
| Tartalék kézi nyugtatömb napi összesítője, ha a Számlázz.hu nem érhető el | [Kézi nyugtatömb](/docs/nav-nyugta/kezi-nyugtatomb) |
A legtöbb webshopnak és szolgáltatónak elég az első és a negyedik: a Számlázz.hu beküld, te pedig egyeztetéssel ellenőrzöd, hogy minden napod megérkezett-e. Beküldeni csak azt kell magadnak, amit nem a Számlázz.hu-ban állítottál ki, például a kézi tartalék tömb nyugtáit.
A `kassza nav summary --file nyugtak.json` parancs ugyanezt a napi összesítőt írja ki egy JSON
fájlba mentett nyugtalistából. Lásd [Parancssor](/docs/kiegeszitok/parancssor).
## Kapcsolódó [#kapcsolódó]
navDailyReports és dailyClose a kiállított nyugtákból.
Technikai felhasználó, lekérdezés, beküldés, javítás és érvénytelenítés.
Ki adhat egyáltalán számítógéppel előállított nyugtát.
# Mi a teendő december 31. előtt?
URL: https://www.kasszajs.hu/docs/nav-nyugta/hatarido
> A NAV nyugta-adatszolgáltatás türelmi ideje 2026. december 31-én lejár. Döntési fa három forgatókönyvre, és hogy mit kell addig a kasszával előkészíteni.
A számítógéppel előállított nyugták adatszolgáltatása 2026. szeptember 1-jétől kötelező, a NAV **2026. december 31-ig** nem szankcionál. Az Agenttel kiállított nyugtákról a Számlázz.hu a NAV összekötés után maga jelent; hogy a fiókodban ez mikortól él, azt a Számlázz.hu tájékoztatásából követheted. Mivel ez a te kötelezettséged marad, érdemes mindhárom lehetséges helyzetre felkészülni.
## Mindhárom esetben kell: bizonylatnapló [#mindhárom-esetben-kell-bizonylatnapló]
A napi összesítőhöz és az egyeztetéshez a kiállított nyugták listája kell, amit a Számla Agent nem tud dátum szerint visszaadni. Ezért az első lépés mindig ugyanaz: kösd be a [bizonylatnaplót](/docs/kiegeszitok/bizonylatnaplo), `onDocumentError: 'throw'` beállítással, hogy egy kimaradt bejegyzés ne maradjon észrevétlen.
## A) A Számlázz.hu időben jelent helyetted [#a-a-számlázzhu-időben-jelent-helyetted]
1. Ne küldd be te is a nyugtákat: az kettős adatszolgáltatás. A kassza NAV kliense alapból csak olvas.
2. Naponta (vagy hetente) [egyeztess](/docs/nav-nyugta/egyeztetes): a naplóból számolt napi összesítőt a `reconcileNavReports()` veti össze a NAV-nál rögzítettekkel.
3. Ha egy nap hiányzik vagy eltér, jelezd a Számlázz.hu-nak.
## B) A türelmi idő lejár, és nincs automatikus jelentés [#b-a-türelmi-idő-lejár-és-nincs-automatikus-jelentés]
1. A [NAV kliens](/docs/nav-nyugta/nav-kliens) írási útvonalával (`allowWrite: true`) magad küldöd be a napi összesítőt, 3 naptári napon belül.
2. Ehhez NAV technikai felhasználó kell. Előtte próbáld végig a teljes kört a NAV **teszt** környezetében: a kassza repójában az `npm run nav:rehearsal` szkript hitelesít, beküld, listáz, módosít és érvénytelenít, és minden lépés eredményét kiírja.
3. Kapcsold ki a beküldést, amint a Számlázz.hu automatikája élesedik (lásd A).
## C) Változik a szabály vagy a határidő [#c-változik-a-szabály-vagy-a-határidő]
1. A napló és a `navDailyReports()` visszamenőleg is újrafuttatható, így egy módosított szabály szerinti összesítő a régi napokra is elkészíthető.
2. A kassza változásfigyelője hetente összeveti a Számlázz.hu NAV-nyugta oldalát és a NAV eRECEIPT specifikációját a kassza által ismerttel, és eltérésnél hibajegyet nyit.
## Ellenőrzőlista [#ellenőrzőlista]
| Teendő | Hol |
| --- | --- |
| Bizonylatnapló bekötve, `onDocumentError: 'throw'` | [Bizonylatnapló](/docs/kiegeszitok/bizonylatnaplo) |
| Befejezetlen tételek rendezése (`journal.settle()`) ütemezve | [Bizonylatnapló](/docs/kiegeszitok/bizonylatnaplo#befejezetlen-tételek-írj-előbb-aztán-állíts-ki) |
| Napi összesítő a naplóból | [Napi összesítő](/docs/nav-nyugta/napi-osszesito) |
| Egyeztetés a NAV-val | [Egyeztetés](/docs/nav-nyugta/egyeztetes) |
| NAV technikai felhasználó és tesztkörnyezeti próba (ha B-re készülsz) | [NAV kliens](/docs/nav-nyugta/nav-kliens) |
| Tartalék kézi nyugtatömb | [Kézi nyugtatömb](/docs/nav-nyugta/kezi-nyugtatomb) |
Ez az oldal a kassza eszközeit rendezi a lehetséges helyzetek szerint. Hogy a te tevékenységedre
pontosan mi vonatkozik, azt a könyvelőddel egyeztesd.
# Napi összesítő
URL: https://www.kasszajs.hu/docs/nav-nyugta/napi-osszesito
> A NAV nyugta-adatszolgáltatás napi összesítője a kiállított nyugtákból a navDailyReports függvénnyel, és napi pénztárzárás a dailyClose függvénnyel.
A `kassza/reports` modul a már kiállított nyugtákból számolja ki azt, amit a NAV napi összesítőként vár. Nem hív hálózatot: a nyugtákat a saját adatbázisodból, a [pénzügyi adatkapcsolatból](/docs/adatkapcsolat) vagy a `receipts.get()` válaszaiból adod át.
## navDailyReports [#navdailyreports]
```ts
import { describeNavReport, navDailyReports } from 'kassza/reports'
const jelentesek = navDailyReports(nyugtak)
for (const jelentes of jelentesek) console.log(describeNavReport(jelentes))
```
```text
Tárgynap: 2026-10-02
Kezdő nyugtasorszám: NYGT-2026-118
Pénznem: HUF
5%: értékesítés 12 400, módosító/érvénytelenítő 0
27%: értékesítés 48 260, módosító/érvénytelenítő -2 540
Végösszeg: 58 120
Nyugták száma: 31, módosító/érvénytelenítő bizonylatok száma: 1
```
A függvény tárgynap, nyugtasorozat, pénznem és árfolyam szerint csoportosít, így egy jelentés pontosan egy NAV-adatszolgáltatásnak felel meg.
| Mező | Jelentés |
| --- | --- |
| `applicableDate` | Tárgynap, a nyugta kelte |
| `series` | A nyugtasorozat, alapból a nyugtaszám utolsó kötőjel előtti része (`NYGT-2026`) |
| `serialNumber` | A nap első nyugtaszáma a sorozatban |
| `currency`, `exchangeRate` | Pénznem és árfolyam (forintnál `null`) |
| `vatCategories` | Áfakategóriánként az értékesítés (`saleDocument`) és a sztornók előjeles összege (`modifyingDocument`) |
| `total` | A kettő algebrai összege |
| `numberOfSaleDocument`, `numberOfModifyingDocument` | Az eladások és a sztornók száma, külön |
| `receiptNumbers` | A jelentésbe számolt nyugták, sorrendben |
A szabályok, amelyeket a függvény betart:
- A sztornónyugta módosító bizonylat: az összege negatívként a `modifyingDocument` mezőbe kerül, és külön darabszámot kap. Egy eladás és a sztornója két bizonylat, nem nullázzák ki egymást.
- Az áfakulcs alapból így képződik le: `0`, `5`, `18` és `27` a saját kategóriájába, `AAM` az „Alanyi adómentes” kategóriába, minden más (például `TAM` vagy egy uniós kulcs) az „Egyéb” kategóriába. Ettől eltérni a `vatCategory` opcióval lehet.
- A tesztfiókban kiállított nyugtákat kihagyja, hacsak nem adod meg az `includeTest: true` opciót.
- Egy nyugtaszámot csak egyszer számol, akkor is, ha kétszer szerepel a listában.
- Devizás nyugtánál árfolyam nélkül hibát ad, mert az összesítőben kötelező.
| Opció | Jelentés |
| --- | --- |
| `includeTest` | A tesztfiókos nyugtákat is összesíti. |
| `vatCategory(tétel, nyugta)` | Saját áfakategória-hozzárendelés, ha a könyvelőd mást mond. |
| `series(nyugtaszám)` | Saját sorozatképzés, ha a nyugtaszámaid más formátumúak. |
A jelentés közvetlenül átadható a [NAV kliens](/docs/nav-nyugta/nav-kliens) `submitReport()` metódusának, vagy az [egyeztetés](/docs/nav-nyugta/egyeztetes) helyi oldalának.
## Napi pénztárzárás [#napi-pénztárzárás]
A `dailyClose()` ugyanazokból a nyugtákból napi zárást készít: a nap eladásait és sztornóit, fizetési módonként (kifizetésenként) és áfakulcsonként. Ez a saját könyvelésedhez és a pénztár egyeztetéséhez való, nem a NAV-nak.
```ts
import { dailyClose } from 'kassza/reports'
for (const zaras of dailyClose(nyugtak)) {
console.log(zaras.date, zaras.currency, zaras.grossTotal)
for (const fizetes of zaras.byPaymentMethod) console.log(fizetes.method, fizetes.amount)
}
```
| Mező | Jelentés |
| --- | --- |
| `date`, `currency` | Nap és pénznem |
| `receiptCount`, `reversalCount` | Eladások és sztornók száma |
| `salesGross`, `reversalsGross`, `grossTotal` | Eladások, sztornók (negatív) és az egyenleg |
| `byPaymentMethod` | Összeg fizetési módonként; több kifizetéses nyugtánál a kifizetések szerint bontva |
| `byVat` | Nettó, áfa és bruttó áfakulcsonként |
| `receiptNumbers` | A napba számolt nyugták |
## Kapcsolódó [#kapcsolódó]
Megérkezett-e minden nap a NAV-hoz, egyezik-e az összeg?
kassza nav summary: ugyanez a terminálban, JSON fájlból.
# NAV kliens
URL: https://www.kasszajs.hu/docs/nav-nyugta/nav-kliens
> A NAV nyugta-adatszolgáltatás gépi interfészének (receipt-if) kliense a kassza/nav modulban. Technikai felhasználó, lekérdezés, beküldés, javítás, érvénytelenítés, kiállító szoftver és hibakezelés.
A `createNavReceiptClient()` a NAV nyugta-adatszolgáltatási interfészét hívja (`/receipt-if`, teszt és éles környezet). Lekérdezi a NAV-nál rögzített napi összesítőket, és ha neked kell beküldeni (például a [kézi nyugtatömb](/docs/nav-nyugta/kezi-nyugtatomb) nyugtáit), beküldi, javítja vagy érvényteleníti őket. Az SHA-512 jelszó-hasht és az SHA3-512 kérésaláírást maga készíti, függőség nélkül.
Ha a nyugtáidat a Számlázz.hu jelenti, ne küldd be őket te is, mert az kettős
adatszolgáltatás. Ezért a kliens alapból nem küld adatot: a beküldő, javító és érvénytelenítő
metódusok `write_blocked` hibát dobnak, amíg nem adod meg az `allowWrite: true` opciót.
## Technikai felhasználó [#technikai-felhasználó]
A géppel történő adatszolgáltatáshoz a NAV Felhasználókezelőjében (UAM) **technikai felhasználó** kell, nyugta-adatszolgáltatási jogosultsággal és aláírókulccsal. Ennek adatai kellenek a klienshez:
```dotenv title=".env.local"
NAV_LOGIN=...
NAV_PASSWORD=...
NAV_SIGNATURE_KEY=...
NAV_TAX_NUMBER=12345676
```
```ts title="lib/nav.ts"
import { createNavReceiptClient } from 'kassza/nav'
export const nav = createNavReceiptClient({
environment: 'production',
login: process.env.NAV_LOGIN!,
password: process.env.NAV_PASSWORD!,
signatureKey: process.env.NAV_SIGNATURE_KEY!,
taxNumber: process.env.NAV_TAX_NUMBER!,
})
```
| Opció | Alapérték | Jelentés |
| --- | --- | --- |
| `environment` | – | `test` vagy `production`. Kötelező, hogy ne küldj véletlenül élesbe. |
| `login`, `password` | – | A technikai felhasználó. A jelszó helyett a nagybetűs SHA-512 hash is megadható (`passwordHash`). |
| `signatureKey` | – | Az aláírókulcs. |
| `taxNumber` | – | A cég adószáma vagy törzsszáma. |
| `predecessorTaxNumber` | – | Jogelőd törzsszáma, ha kell. |
| `softwareName` | – | A kiállító szoftver neve a beküldésekhez. |
| `allowWrite` | `false` | Beküldés, javítás és érvénytelenítés engedélyezése. |
| `timeoutMs` | `15000` | Egy kérés időkorlátja. |
| `fetch`, `baseUrl` | – | Egyedi fetch vagy cím, például teszthez. |
A NAV token 5 percig érvényes. A kliens tárolja és újrahasznosítja, lejárat vagy `401` válasz után egyszer újat kér.
## Lekérdezés [#lekérdezés]
```ts
const lista = await nav.listAllReports({ from: '2026-09-01', to: '2026-09-30' })
for (const sor of lista) {
console.log(sor.applicableDate, sor.serialNumber, sor.totalAmount, sor.status, sor.softwareName)
}
const elso = lista[0]
if (elso) console.log(await nav.getReport(elso.id))
```
A `listReports()` egy oldalt ad vissza (`page`, `pageSize`, rendezés), a `listAllReports()` végiglapozza az időszakot. Az állapot `RECORDED` (érvényes) vagy `INVALIDATED` (érvénytelenített vagy javítással felülírt). A `softwareName` megmutatja, melyik szoftver küldte be, így látod, mit jelentett a Számlázz.hu.
## Beküldés [#beküldés]
Beküldés előtt a kiállító szoftver nevét egyszer rögzíteni kell a NAV-nál, különben `EPGP0002` hibát kapsz:
```ts
const nav = createNavReceiptClient({
environment: 'production',
login: process.env.NAV_LOGIN!,
password: process.env.NAV_PASSWORD!,
signatureKey: process.env.NAV_SIGNATURE_KEY!,
taxNumber: process.env.NAV_TAX_NUMBER!,
softwareName: 'Kézi tömb',
allowWrite: true,
})
await nav.registerSoftware()
const eredmeny = await nav.submitReport(jelentes)
console.log(eredmeny.id, eredmeny.created)
```
A `registerSoftware()` idempotens: ha a név már rögzítve van, nem küld újra. A `submitReport()` beküldés előtt ellenőrzi az adatokat (dátum, sorszám, pénznem, árfolyam, kategóriák, összegek és darabszámok), és megnézi, van-e már érvényes adatszolgáltatás ugyanarra a tárgynapra és kezdő sorszámra:
- ha van, és az adatai egyeznek, nem küld újra, hanem `created: false` eredményt ad,
- ha van, de eltér, `conflict` hibát dob a meglévő azonosítójával. Ilyenkor a javítás a helyes út, nem az újraküldés.
## Javítás és érvénytelenítés [#javítás-és-érvénytelenítés]
```ts
await nav.modifyReport(id, javitottJelentes)
await nav.invalidateReport(id)
```
A javítás új rekordot hoz létre, a régi `INVALIDATED` lesz. A kezdő sorszám nem javítható: ha az hibás, érvénytelenítsd a jelentést, és küldj be újat. Egy rekord csak egyszer javítható (`EPGP0051`), a további javítás a javítással létrejött új rekordra megy.
## Segédlisták [#segédlisták]
A `vatCategories()` a NAV áfakategóriáit adja érvényességi idővel, a `currencies()` a beküldhető pénznemeket. A `kassza/reports` napi összesítője ugyanezeket a kategóriákat használja.
## Hibák [#hibák]
Minden hiba `NavReceiptError`, az `isNavReceiptError()` típusőrrel szűkítheted.
| Kategória | Jelentés |
| --- | --- |
| `auth` | Hibás technikai felhasználó, jelszó vagy aláírókulcs |
| `validation` | Hibás adat (a `fieldErrors` mezőnként megmondja, mi) |
| `business` | Üzleti szabály, például nem rögzített szoftver (`EPGP0002`) |
| `conflict` | Ugyanarra a napra és sorszámra már van eltérő adatszolgáltatás |
| `write_blocked` | Írás `allowWrite: true` nélkül |
| `technical`, `network`, `timeout` | Átmeneti hiba, a `retryable` igaz |
| `configuration`, `unexpected_response` | Beállítási hiba vagy váratlan NAV válasz |
A NAV `EPGP` hibakódja a `code` mezőben van, a magyar javítási tipp a `hint` mezőben.
## Kapcsolódó [#kapcsolódó]
A helyi összesítő és a NAV-nál rögzített adatok összevetése.
Papír nyugták napi jelentése.
# Egyeztetés
URL: https://www.kasszajs.hu/docs/nav-nyugta/egyeztetes
> Ellenőrzés, hogy a NAV-hoz minden nap nyugta-összesítője megérkezett-e, és egyezik-e a kiállított nyugtákkal. reconcileNavReports a helyi összesítő és a NAV lista összevetésére.
Ha a nyugtáidat a Számlázz.hu jelenti a NAV-nak, a te dolgod annyi, hogy ellenőrizd: minden napod megérkezett, és jó adatokkal. A `reconcileNavReports()` összeveti a saját nyugtáidból számolt [napi összesítőt](/docs/nav-nyugta/napi-osszesito) a NAV-nál rögzített adatszolgáltatásokkal.
```ts title="scripts/nav-egyeztetes.ts"
import { reconcileNavReports } from 'kassza/nav'
import { navDailyReports } from 'kassza/reports'
import { nav } from '@/lib/nav'
import { nyugtakIdoszakra } from '@/lib/nyugtak'
const nyugtak = await nyugtakIdoszakra('2026-09-01', '2026-09-30')
const helyi = navDailyReports(nyugtak)
const nalNav = await nav.listAllReports({ from: '2026-09-01', to: '2026-09-30' })
const eredmeny = reconcileNavReports(helyi, nalNav)
for (const hiany of eredmeny.missing) {
console.warn('Nincs a NAV-nál:', hiany.applicableDate, hiany.serialNumber)
}
for (const elteres of eredmeny.mismatched) {
console.warn('Eltér:', elteres.local.applicableDate, elteres.differences.join('; '))
}
```
A NAV klienshez itt csak olvasási jog kell, `allowWrite` nélkül.
| Mező | Jelentés |
| --- | --- |
| `matched` | Egyező napok: a tárgynap és a kezdő sorszám megvan a NAV-nál, a végösszeg és a két darabszám egyezik. |
| `mismatched` | A nap megvan, de eltér; a `differences` magyarul írja le, miben. |
| `missing` | A helyi összesítő napja hiányzik a NAV-nál. |
| `unexpected` | A NAV-nál van olyan érvényes adatszolgáltatás, amelyhez nincs helyi nap. |
A párosítás kulcsa a tárgynap és a kezdő sorszám. Az érvénytelenített (`INVALIDATED`) rekordokat figyelmen kívül hagyja, csak a `RECORDED` állapotúakat veti össze.
A beküldési határidő a kibocsátást követő 3. nap vége. Érdemes naponta futtatni az egy héttel
korábbi napokig visszamenőleg, így a késve beérkező adatszolgáltatás sem okoz téves riasztást,
a hiányra pedig még időben fény derül.
## Kapcsolódó [#kapcsolódó]
Lekérdezés, beküldés és javítás.
A helyi oldal: navDailyReports.
# Kézi nyugtatömb
URL: https://www.kasszajs.hu/docs/nav-nyugta/kezi-nyugtatomb
> Tartalék papír nyugtatömb napi NAV-összesítője a paperReceiptReport függvénnyel, ha a Számlázz.hu nem érhető el, és beküldése a NAV klienssel.
Ha a Számlázz.hu karbantartás vagy hálózati hiba miatt nem érhető el, a helyszíni árusítás nem állhat le: a pénztáros a nyomdai nyugtatömbből ad kézi nyugtát. Ezekről a nyugtákról a Számlázz.hu nem tud, ezért a napi NAV-összesítőt neked kell beküldeni. A `paperReceiptReport()` a tömbből kiadott nyugták adataiból elkészíti az összesítőt.
```ts
import { paperReceiptReport } from 'kassza/nav'
const jelentes = paperReceiptReport({
applicableDate: '2026-10-02',
entries: [
{ number: 'A-0001', vat: '27%', gross: 1_780 },
{ number: 'A-0002', vat: '27%', gross: 890 },
{ number: 'A-0002', vat: '5%', gross: 500 },
{ number: 'A-0003', vat: '27%', gross: -890, modifying: true },
],
})
const eredmeny = await nav.submitReport(jelentes)
```
| Mező | Jelentés |
| --- | --- |
| `applicableDate` | A tárgynap. |
| `currency`, `exchangeRate` | Pénznem, alapból `HUF`; devizánál az árfolyam is. |
| `entries[].number` | A nyugta sorszáma. Egy nyugta több sort kaphat, ha több áfakategóriát tartalmaz. |
| `entries[].vat` | NAV-áfakategória: `0%`, `5%`, `18%`, `27%`, `Alanyi adómentes` vagy `Egyéb`. |
| `entries[].gross` | Bruttó összeg; eladásnál nem lehet negatív. |
| `entries[].modifying` | `true` a módosító vagy érvénytelenítő bizonylatnál (visszavétel). |
A darabszámokat nyugtánként számolja, nem soronként, és a kezdő sorszám a legkisebb sorszám lesz. Ugyanaz a sorszám nem lehet egyszerre eladás és módosító bizonylat.
A beküldéshez a [NAV kliens](/docs/nav-nyugta/nav-kliens#beküldés) kell `allowWrite: true` beállítással és egy rögzített kiállító szoftver névvel (például „Kézi tömb”). Egy tömb egy sorozatnak számít, ezért a Számlázz.hu nyugtáitól elkülönülő sorszámokat használj.
A kliens `maintenanceCooldownMs` opciójával a kassza karbantartás után azonnal hibát ad, ahelyett
hogy várakozna, így a pénztárprogram gyorsan átválthat a kézi tömbre. Lásd
[Pontosan egyszer](/docs/alapok/pontosan-egyszer#karbantartási-szünet).
## Kapcsolódó [#kapcsolódó]
Beküldés, javítás és érvénytelenítés.
A nyugták fajtái és a pénztárgép-kötelezettség.
# Megbízotti számlázás
URL: https://www.kasszajs.hu/docs/megbizott-szamlazas
> Platformoknak, amelyek sok cég nevében állítanak ki számlát vagy nyugtát. A megbízói fiók csatlakoztatása, a kapcsolat állapotának ellenőrzése és megbízónkénti kassza kliensek a kassza/delegation modulban.
A megbízotti számlakibocsátással egy platform (a **megbízott**) más cégek (a **megbízók**) nevében állít ki bizonylatot, a megbízó saját Számlázz.hu fiókjában. Ilyen egy foglalási rendszer, egy piactér vagy egy POS-szolgáltatás, amely minden kereskedőjének a saját nevében számláz.
A `kassza/delegation` modul a teljes folyamatot lefedi:
1. **Csatlakoztatás:** a megbízott a saját Agent kulcsával létrehoz vagy összekapcsol egy megbízói fiókot, és hozzá egy dedikált felhasználót ([`connectPrincipal`](/docs/megbizott-szamlazas/csatlakozas)).
2. **Jóváhagyás:** a megbízó birtokba veszi az új fiókot, vagy elfogadja a csatlakozási kérelmet. Ezt a kassza nem tudja helyette megtenni.
3. **Ellenőrzés:** a megbízott megnézi, él-e már a kapcsolat ([`probeDelegation`](/docs/megbizott-szamlazas/allapot)).
4. **Kiállítás:** a megbízott a dedikált felhasználóval, a megbízó előtagjával állít ki bizonylatot ([`createDelegateKassza`, `createKasszaPool`](/docs/megbizott-szamlazas/kliensek)).
A fiókok Agenten keresztüli létrehozását (`action-agent_ceg_mb`) a Számlázz.hu ügyfélszolgálata
engedélyezi, tesztfiókra is. Engedély nélkül 137-es hibát kapsz.
## Miért dedikált felhasználó, és miért nem Agent kulcs? [#miért-dedikált-felhasználó-és-miért-nem-agent-kulcs]
Az Agent kulcs egy fiókhoz tartozik. Ha a megbízott a megbízó Agent kulcsával állítana ki számlát, a bizonylat a megbízó saját kiállításaként készülne, nem megbízottiként. Ezért a megbízotti kiállításhoz a csatlakozáskor létrehozott **dedikált felhasználó** felhasználónevét és jelszavát kell használni, megbízónként külön. A kassza ezt ki is kényszeríti: a megbízotti kliens Agent kulccsal hibát ad.
A dedikált felhasználó csak egy fiókhoz férhet hozzá. Ha több fiókhoz is hozzáfér, a Számlázz.hu 164-es hibával elutasítja.
## Előtagok [#előtagok]
Minden megbízóhoz egyeztetett számlaszám-előtag tartozik (legfeljebb 5 nagybetű vagy szám). A megbízotti kliens ezt minden számlában elküldi, mert a Számlázz.hu megbízotti kiállításnál megköveteli (357). Ha az előtag nem megfelelő, a Számlázz.hu néha megmondja, melyiket kell használni (356): ezt a `suggestedPrefix(error)` olvassa ki a hibából.
```ts
import { suggestedPrefix } from 'kassza/delegation'
const javasolt = suggestedPrefix(error)
```
## Megbízotti korlátozások [#megbízotti-korlátozások]
Néhány művelet csak a megbízottnak vagy csak a fiók tulajdonosának engedélyezett. A megbízotti számlát a tulajdonos nem sztornózhatja (359), a megbízott pedig nem sztornózhatja a tulajdonos saját számláját (358). Ugyanez a helyzet a számla ismétlésével (489) és helyesbítésével (493, 494). Ezek a hibák `account` kategóriájúak, a kassza magyar tippet ad hozzájuk.
## Kapcsolódó [#kapcsolódó]
Megbízói fiók létrehozása vagy összekapcsolása.
Él-e már a megbízotti kapcsolat?
createDelegateKassza és createKasszaPool.
# Csatlakozás
URL: https://www.kasszajs.hu/docs/megbizott-szamlazas/csatlakozas
> Megbízói Számlázz.hu fiók létrehozása vagy összekapcsolása a connectPrincipal függvénnyel. A négy lehetséges kimenet, az adószám ellenőrzése és az előtagok.
A `connectPrincipal()` a megbízott saját hitelesítésével (Agent kulcs vagy felhasználónév és jelszó) küldi el a megbízó cégadatait és a dedikált felhasználó adatait. A Számlázz.hu az adószám alapján dönti el, hogy új fiókot hoz létre, vagy a meglévőhöz kér csatlakozást.
```ts title="lib/csatlakozas.ts"
import { connectPrincipal } from 'kassza/delegation'
const eredmeny = await connectPrincipal(
{
principal: {
name: 'Példa Kft.',
taxNumber: '12345676-2-42',
invoicePrefix: 'PLDA',
zip: '1111',
city: 'Budapest',
address: 'Fő utca 1.',
email: 'penzugy@pelda.hu',
},
user: {
email: 'kassza+pelda@platform.hu',
password: process.env.DELEGATE_PASSWORD!,
firstName: 'Platform',
lastName: 'Számlázás',
},
},
{ verifyTaxNumber: true },
)
console.log(eredmeny.status, eredmeny.message)
```
A megbízott Agent kulcsát a kassza a `SZAMLAZZ_AGENT_KEY` környezeti változóból veszi, de az `agentKey` opcióban is megadhatod. A dedikált felhasználó e-mail címe legyen egyedi megbízónként, a jelszava 8 és 128 karakter közötti. Ezeket a hitelesítő adatokat titkosítva tárold, ezekkel fogsz kiállítani.
## A négy kimenet [#a-négy-kimenet]
| `status` | Mit jelent | Teendő |
| --- | --- | --- |
| `account-created` | Új fiók jött létre a megbízónak, a dedikált felhasználóval. | A megbízó e-mailt kap, és birtokba veszi a fiókot. Addig a kiállítás 250-es hibát ad. |
| `owner-invite-resent` | A fiók már létezik, de még nincs tulajdonosa; a Számlázz.hu újraküldte a meghívót. | Várj a megbízóra. |
| `join-request-sent` | A fióknak van tulajdonosa; a Számlázz.hu csatlakozási kérelmet küldött neki. | A megbízó elfogadja a kérelmet. Addig a belépés 3-as hibát ad. |
| `join-request-resent` | A csatlakozási kérelmet a Számlázz.hu újraküldte. | Várj a megbízóra. |
Ha a Számlázz.hu ismeretlen szöveggel válaszol, a `status` értéke `unknown`, és a teljes szöveg a `message` mezőben van.
Minden hívás újabb e-mailt küld a megbízónak (meghívót vagy csatlakozási kérelmet). A kapcsolat
állapotát a [`probeDelegation()`](/docs/megbizott-szamlazas/allapot) függvénnyel ellenőrizd,
ritkán, ne ezzel.
## Adószám-ellenőrzés [#adószám-ellenőrzés]
A `verifyTaxNumber: true` opcióval a kassza a csatlakozás előtt lekérdezi az adószámot a NAV-tól (a Számlázz.hu adószám-lekérdezésével), és érvénytelen adószámnál el sem küldi a kérést. Egy elgépelt adószám ugyanis új, felesleges fiókot hozna létre.
## Hibák [#hibák]
| Kód | Jelentés |
| --- | --- |
| `68` | Ezzel az adószámmal és előtaggal már van cég: egyeztess másik előtagot. |
| `101` | A dedikált felhasználó e-mail címe már foglalt. |
| `137` | A megbízott fiókjában nincs engedélyezve a fiókok Agenten keresztüli létrehozása. |
| `200`, `362` | Az előtagot ez az adószám vagy egy másik fiók már használja. |
| `506`, `507` | A jelszó túl rövid vagy túl hosszú. |
## Kapcsolódó [#kapcsolódó]
Mikor lehet már kiállítani?
Kiállítás a dedikált felhasználóval.
# A kapcsolat állapota
URL: https://www.kasszajs.hu/docs/megbizott-szamlazas/allapot
> A probeDelegation ellenőrzi, hogy a dedikált felhasználó be tud-e már lépni a megbízó fiókjába, és ha nem, miért nem.
A csatlakozás után a megbízónak lépnie kell: birtokba venni az új fiókot, vagy elfogadni a csatlakozási kérelmet. A `probeDelegation()` a dedikált felhasználó adataival megnézi, hol tart a folyamat. Egy nem létező számla PDF-jét kéri le, így bizonylatot nem hoz létre és nem is módosít.
```ts
import { probeDelegation } from 'kassza/delegation'
const allapot = await probeDelegation({
username: 'kassza+pelda@platform.hu',
password: process.env.DELEGATE_PASSWORD!,
})
console.log(allapot.state, allapot.message)
```
| `state` | Jelentés |
| --- | --- |
| `active` | A kapcsolat él, lehet kiállítani. |
| `awaiting-approval` | A belépés sikertelen (3): a megbízó még nem fogadta el a csatlakozási kérelmet, vagy hibás a jelszó. |
| `awaiting-owner-registration` | A megbízó még nem vette birtokba az új fiókot (250). |
| `multi-account-user` | A dedikált felhasználó több fiókhoz is hozzáfér (164), így Agenttel nem használható. |
Más hiba (például hálózati hiba) esetén a függvény a `SzamlazzError`-t dobja tovább.
A 250-es állapotnál a Számlázz.hu minden belépési kísérletre újraküldi a megbízónak a
fiókgazdai meghívót. Ne ellenőrizd ciklusban: elég, ha a megbízó a felületeden jelzi, hogy
végzett, vagy ha napi egyszer megnézed.
A függvény Agent kulccsal hibát ad, mert az Agent kulcs a fiókhoz tartozik, nem a megbízotti kapcsolathoz.
## Kapcsolódó [#kapcsolódó]
A négy kimenet és a hibakódok.
Kiállítás, ha a kapcsolat él.
# Kliensek megbízónként
URL: https://www.kasszajs.hu/docs/megbizott-szamlazas/kliensek
> Kiállítás a megbízó nevében a createDelegateKassza klienssel, és sok megbízó kiszolgálása egy folyamatból a createKasszaPool LRU poollal.
## createDelegateKassza [#createdelegatekassza]
A `createDelegateKassza()` ugyanazt a `Kassza` felületet adja, mint a `createKassza()`, de a dedikált felhasználóval hitelesít, és a megbízó előtagját minden számlában elküldi.
```ts
import { createDelegateKassza } from 'kassza/delegation'
const kassza = createDelegateKassza({
username: 'kassza+pelda@platform.hu',
password: process.env.DELEGATE_PASSWORD!,
invoicePrefix: 'PLDA',
receiptPrefix: 'PLDANY',
})
await kassza.invoices.createOnce({
orderNumber: 'FOGLALAS-881',
buyer: { name: 'Vendég Anna', zip: '1111', city: 'Budapest', address: 'Fő utca 2.' },
items: [{ name: 'Szállás, 2 éj', grossUnitPrice: 64_000, vat: 18 }],
})
```
| Opció | Jelentés |
| --- | --- |
| `username`, `password` | A dedikált felhasználó. Agent kulcs nem adható meg. |
| `invoicePrefix` | A megbízóval egyeztetett számlaszám-előtag (legfeljebb 5 nagybetű vagy szám). |
| `receiptPrefix` | A nyugták előtagja, ha a megbízó nevében nyugtát is adsz. |
| `defaults` | További alapértelmezések számlára és nyugtára. |
| a `createKassza()` többi opciója | Munkamenet-tároló, próbálkozásnapló, időkorlát, hookok, fetch. |
## createKasszaPool [#createkasszapool]
Egy platform egyszerre sok megbízót szolgál ki. A `createKasszaPool()` megbízó-azonosító szerint ad klienst: az elsőt a `resolve` függvényed adataiból hozza létre, utána újrahasznosítja, így a munkamenet is megmarad.
```ts title="lib/kassza-pool.ts"
import { createKasszaPool } from 'kassza/delegation'
import { upstashRedisCookieStore } from 'kassza/cookie-stores'
import { Redis } from '@upstash/redis'
import { megbizoAdatai } from '@/lib/megbizok'
const redis = Redis.fromEnv()
export const pool = createKasszaPool({
resolve: async (megbizoId) => {
const megbizo = await megbizoAdatai(megbizoId)
if (!megbizo) return undefined
return {
username: megbizo.felhasznalo,
password: megbizo.jelszo,
invoicePrefix: megbizo.elotag,
}
},
cookieStore: upstashRedisCookieStore(redis),
attemptLedger: upstashRedisCookieStore(redis),
})
```
```ts
const kassza = await pool.get('kereskedo-42')
await kassza.receipts.createOnce({
orderNumber: 'R-1001',
paymentMethod: 'bankkártya',
items: [{ name: 'Kávé', grossUnitPrice: 890, vat: 27 }],
})
```
- A pool legfeljebb `maxClients` (alapból 100) klienst tart, a legrégebben használtat dobja el először.
- Ugyanarra a megbízóra egyszerre érkező kérések ugyanazt a klienst kapják; a `resolve` csak egyszer fut.
- Ha a `resolve` nem talál megbízót (`undefined`) vagy hibát dob, a `get()` hibát ad, és a hibát nem tárolja: a következő hívás újra próbálkozik.
- Ha a megbízó jelszava vagy előtagja változik, a `pool.forget(megbizoId)` eldobja a régi klienst. A `pool.clear()` mindet eldobja.
A közös opciók (munkamenet-tároló, próbálkozásnapló, időkorlát, hookok) minden megbízói kliensre érvényesek. A munkamenetet a kassza felhasználónként külön kulccsal tárolja, így a megbízók munkamenetei nem keverednek.
## Kapcsolódó [#kapcsolódó]
Él-e a kapcsolat, mielőtt kiállítasz?
createOnce és kitiltás elleni védelem.
# Pénzügyi adatkapcsolat
URL: https://www.kasszajs.hu/docs/adatkapcsolat
> A Számlázz.hu pénzügyi adatkapcsolat (PUSH) fogadása a kassza/data-link kezelővel. Kimenő és bejövő számlák, banki tranzakciók és nyugták, azonosító kulcsok, iktatószám és helyes válasz XML.
A pénzügyi adatkapcsolattal a Számlázz.hu magától átküldi a fogadó rendszernek (például egy könyvelőprogramnak vagy ERP-nek) a kiállított és befogadott számlákat, a banki tranzakciókat és a nyugtákat. Az átküldés PUSH jellegű: a Számlázz.hu hív téged, lekérdezni nem lehet.
A `dataLinkHandler()` fogadja ezeket a kéréseket, ellenőrzi az azonosító kulcsot, a bizonylatot kassza típusokká alakítja, és a Számlázz.hu által elvárt válasz XML-t is elkészíti.
```ts title="app/api/szamlazz/adatkapcsolat/route.ts"
import { dataLinkHandler } from 'kassza/data-link'
import { bizonylatMentese, ugyfelKulcsai } from '@/lib/konyveles'
export const POST = dataLinkHandler({
verifyKey: async (kulcs) => (await ugyfelKulcsai()).includes(kulcs),
onPush: async (push) => {
const iktatoszam = await bizonylatMentese(push)
return { registrationNumber: iktatoszam }
},
})
```
## Beállítás [#beállítás]
1. A fogadó rendszeredet a Számlázz.hu-nál regisztráltatni kell: ehhez vedd fel velük a kapcsolatot. A regisztrációkor adod meg a fogadó URL-t, és itt dől el az is, kérsz-e PDF-et az üzenetekben.
2. Az ügyfeleidnek te adsz azonosító kulcsot (legfeljebb 40 karakter). A kulcs utolsó 5 karaktere a rendszeredre jellemző, a regisztrációkor rögzített végződés: a Számlázz.hu csak ilyen kulcsot enged beállítani.
3. Az ügyfél a Számlázz.hu fiókjában a **Beállítások / Fiók beállításai / Online pénzügyi adatkapcsolat** menüben kiválasztja a rendszeredet, és beírja a kulcsot. A szolgáltatáshoz #profi csomag kell; tesztfiókban ez alapból adott.
Minden ügyfél ugyanarra az URL-re küld, az `X-Szamlazzhu-Key` fejlécben a saját kulcsával. Aláírás nincs, ezért a kulcs ellenőrzése kötelező: a `keys` (fix lista) vagy a `verifyKey` (például adatbázisból) opció nélkül a kezelő el sem indul.
## Üzenetek [#üzenetek]
Egy kérés egy bizonylatot hoz. Számla a kiállítás után 15 percen belül érkezik, a banki tranzakciók kötegelve, a nyugták naponta egyszer. A `push.kind` szerint:
| `kind` | Tartalom |
| --- | --- |
| `invoice` | Kimenő számla: `invoice` (ugyanaz az `InvoiceDetails`, mint az `invoices.get()` válasza, a PDF-fel együtt, ha kérted), `id`, `deleted` |
| `incoming-invoice` | Befogadott számla ugyanígy; a `deleted` jelzi, ha törölték |
| `bank-transaction` | `transaction`: bankszámla, értéknap, irány (`in` vagy `out`), összeg, pénznem, partner, közlemény |
| `receipts` | `receipts`: a nap nyugtái (`Receipt`) a kibocsátó adószámával |
A Számlázz.hu egy számlát többször is átküldhet, például befizetés vagy sztornó után. A
frissítéshez az `id` mezőt használd kulcsnak (a Számlázz.hu is ezt ajánlja), ne a
számlaszámot és ne az iktatószámot.
## Válasz [#válasz]
A kezelő a helyes válasz XML-t magától elkészíti, a kimenő és bejövő számláknál a kötelező `id` visszaküldésével együtt. Az `onPush` visszatérési értéke opcionális:
| Mező | Jelentés |
| --- | --- |
| `registrationNumber` | Iktatószám számlákhoz. A Számlázz.hu eltárolja, és a későbbi újraküldésekben visszaküldi; a PDF-en nem jelenik meg. |
| `keyError` | `KEY_ERR` vagy `KEY_DEL`, ha az `onPush` dönt a kulcsról. |
## Kulcshibák [#kulcshibák]
| Válasz | Mit tesz a Számlázz.hu |
| --- | --- |
| `KEY_ERR` | A kulcs ismeretlen; a bizonylatot a következő változásáig nem küldi újra. A kezelő ezt adja, ha a kulcs hiányzik, vagy nincs a `keys` listában, vagy a `verifyKey` `false`-t ad. |
| `KEY_DEL` | A kulcs törlendő: a Számlázz.hu kikapcsolja az adatkapcsolatot az ügyfél fiókjában, és e-mailben értesíti. Akkor használd, ha az ügyfél megszűnt nálad. A `verifyKey` ezt `'KEY_DEL'` visszaadásával kéri. |
## Hibák és újraküldés [#hibák-és-újraküldés]
| Helyzet | Válasz | Következmény |
| --- | --- | --- |
| Hibás vagy ismeretlen XML, túl nagy törzs (alapból 64 MB felett) | `400` | A Számlázz.hu újrapróbálja. |
| Az `onPush` vagy a `verifyKey` hibát dobott | `500` | A Számlázz.hu újrapróbálja; a hibát az `onError` kapja. |
| Sikeres feldolgozás | `200` és válasz XML | Kész. |
A Számlázz.hu 72 órán át próbálkozik, utána a bizonylat kikerül a küldési sorból. Ha a végpont hosszan hibás, lassú vagy nem válaszol, a Számlázz.hu az egész fogadó rendszert leállítja, és csak kérésre kapcsolja vissza. Ezért az `onPush` legyen gyors: mentsd el a bizonylatot, a nehezebb feldolgozást végezd háttérben. Átirányítással (például HTTP 302) válaszoló végpont nem működik.
## Kapcsolódó [#kapcsolódó]
Az adatkapcsolaton érkező nyugtákból is számolható.
Az InvoiceDetails mezői.
# Kiegészítők
URL: https://www.kasszajs.hu/docs/kiegeszitok
> A kassza alcsomagjai a számlázás körüli munkához. Mock kliens és hamis Számla Agent tesztekhez, validátorok, pénzszámítás, PDF tárhely, serverless futtatás, parancssor, MCP szerver és AI asszisztensek.
A fő `kassza` modul a Számla Agent hívásait intézi. A körülötte lévő feladatokra külön alcsomagok vannak, amelyeket csak akkor importálsz, ha kellenek. Egyiknek sincs futásidejű függősége, és a `kassza/storage/fs` kivételével egyik sem használ Node.js API-t.
| Alcsomag | Mire való? |
| --- | --- |
| `kassza/testing` | Mock kliens és hamis Számla Agent hálózat és Agent kulcs nélküli tesztekhez |
| `kassza/validators` | Adószám, bankszámla, IBAN, EU adószám, e-mail és cím ellenőrzése |
| `kassza/money` | A számlán használt kerekítés és összegzés a saját kódodban |
| `kassza/storage`, `kassza/storage/fs` | A bizonylat PDF mentése S3-ba, R2-be, Vercel Blobba, Supabase-be vagy lemezre |
| `kassza/cookie-stores` | Közös munkamenet-tároló serverless és edge környezethez |
| `kassza/ipn` | A Számlázz.hu fizetési értesítéseinek fogadása |
| `kassza/payments` és szolgáltatónként `kassza/payments/stripe`, `…/simplepay`, `…/barion`, `…/revolut`, `…/paypal` | [Fizetésből bizonylat](/docs/fizetesek): webhookok és `issueForPayment` |
| `kassza/reports`, `kassza/nav` | [NAV nyugta-adatszolgáltatás](/docs/nav-nyugta): napi összesítő, NAV kliens, egyeztetés |
| `kassza/delegation` | [Megbízotti számlázás](/docs/megbizott-szamlazas) sok cég nevében |
| `kassza/data-link` | A [pénzügyi adatkapcsolat](/docs/adatkapcsolat) fogadása |
| `kassza/mcp` | [MCP szerver](/docs/kiegeszitok/mcp-szerver) AI asszisztenseknek, saját átvitellel |
A csomag egy `kassza` parancsot is ad (`npx kassza`), lásd [Parancssor](/docs/kiegeszitok/parancssor).
createMockKassza: hívásnapló, szimulált hibák és Vitest példák valódi API nélkül.
createFakeAgentFetch: a valódi kliens tesztje elveszett válasszal, karbantartással és a Számlázz.hu szabályaival.
Adószám CDV, bankszámla, IBAN, EU adószám, e-mail és magyar cím feldolgozása.
Tételösszegek, áfabontás és kerekítés pontosan úgy, ahogy a számlára kerül.
storePdf, dátum szerinti kulcsok és adapterek S3-hoz, R2-höz, Vercel Blobhoz és társaikhoz.
Vercel, Cloudflare Workers, Deno, Bun és AWS Lambda: munkamenet, időkorlát, webhookok.
Az agents mappa és az llms.txt használata Claude Code, Cursor és Copilot mellett.
kassza doctor, verify, xml preview, lekérdezések és napi NAV-összesítő.
Számla és nyugta AI asszisztensből, előnézettel és megerősítéssel.
# Tesztelés
URL: https://www.kasszajs.hu/docs/kiegeszitok/teszteles
> Mock kliens a kassza/testing modulból. Hívásnapló, memóriában tárolt bizonylatok, szimulált hibák, Vitest példák és a kliens injektálása.
Automata tesztben ne hívd a Számlázz.hu-t. A tesztfiók 10 percenként legfeljebb 500 számlát enged, a hálózat lassú és kiszámíthatatlan, és egy elrontott teszt éles fiókon valódi számlát állít ki. Erre való a `createMockKassza()`: ugyanazt a `Kassza` felületet adja, mint a `createKassza()`, de nem hív hálózatot, és nem kell hozzá Agent kulcs.
```ts
import { createMockKassza } from 'kassza/testing'
const kassza = createMockKassza()
const szamla = await kassza.invoices.create({
orderNumber: 'REND-1001',
buyer: { name: 'Vevő Kft.', zip: '1111', city: 'Budapest', address: 'Fő utca 1.' },
items: [{ name: 'Termék', quantity: 2, grossUnitPrice: 5_990, vat: 27 }],
})
console.log(szamla.number, szamla.grossTotal, kassza.calls)
```
Ha a webhook-kezelést, az újrapróbálást vagy a hibatűrést is tesztelnéd, használd a
[hamis Számla Agentet](/docs/kiegeszitok/hamis-agent): az a valódi klienst futtatja végig,
és elveszett választ, karbantartást vagy részleges sikert is tud szimulálni.
A mock ugyanazt a kliensoldali validációt és kerekítést futtatja, mint az éles kliens. Egy ismeretlen áfakulcs vagy egy kisbetűs nyugta előtag itt is `validation` hibát dob, a fenti számla bruttó összege pedig itt is 11 980 Ft.
## A kliens injektálása [#a-kliens-injektálása]
A mockot csak akkor tudod átadni, ha a kódod nem maga hozza létre a klienst. A legegyszerűbb, ha a számlázó függvény paraméterben kapja meg a `Kassza` típusú klienst:
```ts title="lib/szamlazas.ts"
import { type InvoiceBuyer, isSzamlazzError, type Kassza } from 'kassza'
export interface Rendeles {
readonly id: number
readonly vevo: InvoiceBuyer
readonly vegosszeg: number
}
const BIZONYTALAN = ['network', 'timeout', 'partial_success', 'duplicate']
export async function szamlazRendelest(kassza: Kassza, rendeles: Rendeles): Promise {
const orderNumber = `REND-${rendeles.id}`
const meglevo = await kassza.invoices.find({ orderNumber })
if (meglevo) return meglevo.header.number
try {
const szamla = await kassza.invoices.create({
orderNumber,
paid: true,
paymentMethod: 'bankkártya',
buyer: rendeles.vevo,
items: [{ name: 'Rendelés', grossUnitPrice: rendeles.vegosszeg, vat: 27 }],
})
return szamla.number
} catch (error) {
if (isSzamlazzError(error) && BIZONYTALAN.includes(error.category)) {
const letrejott = await kassza.invoices.find({ orderNumber })
if (letrejott) return letrejott.header.number
}
throw error
}
}
```
Élesben a route handler adja át a megosztott klienst, tesztben a mockot:
```ts title="app/api/rendeles/route.ts"
import { kassza } from '@/lib/kassza'
import { szamlazRendelest } from '@/lib/szamlazas'
export async function POST(request: Request) {
const rendeles = await request.json()
const szamlaszam = await szamlazRendelest(kassza, rendeles)
return Response.json({ szamlaszam })
}
```
A `createKassza()` már létrehozáskor `configuration` hibát dob, ha nincs Agent kulcs. Ha a
tesztelt modul importálja a `lib/kassza.ts`-t, a teszt emiatt már az importnál elszáll. Ilyenkor
injektáld a klienst, vagy cseréld le a modult `vi.mock()`-kal (lásd lent).
## Vitest példák [#vitest-példák]
Egy mock példányt a fájl tetején hozol létre, és minden teszt előtt a `reset()` kiüríti a hívásnaplót, a bizonylatokat, a beállított hibákat és a sorszámokat.
```ts title="lib/szamlazas.test.ts"
import { SzamlazzError } from 'kassza'
import { createMockKassza } from 'kassza/testing'
import { beforeEach, describe, expect, test } from 'vitest'
import { type Rendeles, szamlazRendelest } from './szamlazas'
const kassza = createMockKassza({ now: () => new Date('2026-09-17T10:00:00Z') })
const rendeles: Rendeles = {
id: 1001,
vevo: { name: 'Nagy Péter', zip: '1111', city: 'Budapest', address: 'Fő utca 1.' },
vegosszeg: 12_700,
}
beforeEach(() => kassza.reset())
describe('szamlazRendelest', () => {
test('egy rendelésre csak egy számlát állít ki', async () => {
const elso = await szamlazRendelest(kassza, rendeles)
const masodik = await szamlazRendelest(kassza, rendeles)
expect(elso).toBe('E-KASSZA-2026-1')
expect(masodik).toBe(elso)
expect(kassza.calls.filter((hivas) => hivas.method === 'invoices.create')).toHaveLength(1)
expect(kassza.invoiceRecords.get(elso)?.details.totals.grossAmount).toBe(12_700)
})
test('hálózati hibánál továbbdobja a hibát, ha nem készült számla', async () => {
kassza.failNext('invoices.create')
await expect(szamlazRendelest(kassza, rendeles)).rejects.toMatchObject({ category: 'network' })
expect(kassza.invoiceRecords.size).toBe(0)
})
test('fiókhibánál nem kérdez le újra', async () => {
kassza.failNext(
'invoices.create',
new SzamlazzError('[136] Lejárt előfizetés', { category: 'account', code: 136 }),
)
await expect(szamlazRendelest(kassza, rendeles)).rejects.toMatchObject({ code: 136 })
expect(kassza.calls.map((hivas) => hivas.method)).toEqual(['invoices.get', 'invoices.create'])
})
})
```
Ha a kódod egy modul szintű klienst importál, a Vitest `vi.mock()` függvényével cserélheted mockra. A gyár függvényben dinamikusan importáld a `kassza/testing` modult, mert a `vi.mock()` a fájl importjai elé kerül:
```ts title="lib/webhook.test.ts"
import type { MockKassza } from 'kassza/testing'
import { beforeEach, expect, test, vi } from 'vitest'
import { kassza } from './kassza'
import { webhookKezelo } from './webhook'
vi.mock('./kassza', async () => {
const { createMockKassza } = await import('kassza/testing')
return { kassza: createMockKassza() }
})
const mock = kassza as MockKassza
beforeEach(() => mock.reset())
test('a fizetési webhook számlát állít ki', async () => {
await webhookKezelo({ rendelesId: 1001 })
expect(mock.calls.map((hivas) => hivas.method)).toContain('invoices.create')
})
```
## Opciók [#opciók]
| Opció | Típus | Alapérték | Leírás |
| --- | --- | --- | --- |
| `defaults` | `KasszaDefaults` | – | Ugyanaz, mint a `createKassza()` `defaults` opciója, például számla előtag vagy nyugta fizetési mód. |
| `taxpayers` | `Record` | – | A `taxpayer.query()` válaszai a 8 jegyű törzsszám szerint. |
| `credentialsValid` | `boolean` | `true` | A `verifyCredentials()` visszatérési értéke. |
| `now` | `() => Date` | `() => new Date()` | Az aktuális idő. Ebből lesz a kelt, a teljesítés dátuma és a számlaszám éve. |
A dátumok a mockban is budapesti idő szerint számolódnak: `now: () => new Date('2026-09-16T23:30:00Z')` mellett a számla kelte már `2026-09-17`.
```ts
import { createMockKassza } from 'kassza/testing'
const kassza = createMockKassza({
defaults: { invoice: { prefix: 'WEB' }, receipt: { prefix: 'NYGT', paymentMethod: 'bankkártya' } },
taxpayers: { '13421739': { valid: true, name: 'KBOSS.HU KFT.', addresses: [] } },
credentialsValid: false,
})
const ceg = await kassza.taxpayer.query('13421739-2-41')
const ismeretlen = await kassza.taxpayer.query('11111111-2-42')
const kulcsJo = await kassza.verifyCredentials()
```
Itt a `ceg.valid` értéke `true`, az `ismeretlen` értéke `{ valid: false, addresses: [] }`, a `kulcsJo` pedig `false`. A `defaults` miatt a következő számla száma `E-WEB-2026-1` lesz.
## A mock felülete [#a-mock-felülete]
A `MockKassza` a `Kassza` összes metódusán felül ezeket adja:
| Mező | Típus | Leírás |
| --- | --- | --- |
| `calls` | `readonly MockCall[]` | Minden hívás sorrendben, `{ method, args }` alakban. A hibára futott hívás is benne van. |
| `invoiceRecords` | `ReadonlyMap` | A számlák számlaszám szerint: `input`, `details`, `payments`, `reversed`, `deleted`. |
| `receiptRecords` | `ReadonlyMap` | A nyugták nyugtaszám szerint: `receipt` és `sentTo`, a kiküldött e-mail címek hívásonként. |
| `failNext(method, error?)` | `void` | A megadott metódus következő hívása hibát dob. Hiba nélkül egy `network` kategóriájú `SzamlazzError`-t. |
| `reset()` | `void` | Mindent kiürít, a sorszámozás újraindul. |
A `method` nevek az API útvonalát követik:
| Metódusnév | Melyik hívás |
| --- | --- |
| `invoices.create`, `invoices.preview`, `invoices.reverse` | Számla, előnézet, sztornó |
| `invoices.registerPayment`, `invoices.clearPayments` | Befizetések |
| `invoices.getPdf`, `invoices.get`, `invoices.deleteProforma` | PDF, számla adatai, díjbekérő törlése |
| `receipts.create`, `receipts.reverse`, `receipts.get`, `receipts.send` | Nyugták |
| `taxpayer.query`, `verifyCredentials`, `resetSession` | Adószám, kulcs ellenőrzése, munkamenet |
Az `invoices.find()` a naplóban `invoices.get`, a `receipts.find()` pedig `receipts.get`
néven szerepel, és a `failNext('invoices.get')` a `find` hívást is hibára futtatja.
A `failNext()` nem ellenőrzi a metódusnevet. Elírt névnél (például `invoice.create`) a hiba
soha nem következik be, és a teszt tévesen zöld marad. Egy metódushoz egyszerre egy hibát
állíthatsz be, a következő `failNext()` felülírja.
## Mit szimulál a mock? [#mit-szimulál-a-mock]
| Viselkedés | A mockban |
| --- | --- |
| Számlaszám | `E-KASSZA-2026-1`, díjbekérőnél `D-KASSZA-2026-1`, előtaggal `E-WEB-2026-1` |
| Sztornó | Új `E-STORNO-2026-1` számla, az eredetin `reversed: true` |
| Nyugtaszám | `NYGT-2026-1`, sztornónál `NYGT-STORNO-2026-1` |
| PDF | A `MOCK_PDF` néven exportált minimális PDF, ha a `downloadPdf` nincs kikapcsolva |
| Befizetés | A `registerPayment` hozzáad vagy felülír, a hátralékot a bruttó végösszegből számolja |
| Nem létező számla | `[7]`, `not_found`, a `find` `null`-t ad |
| Nem létező díjbekérő törlése | `[335]`, `not_found` |
| Ismételt nyugta `callId` | `[338]`, `duplicate` |
| Nem létező nyugta | `[339]`, `not_found`, a `find` `null`-t ad |
| Kiállított számla fejléce | `test: true`, eladó: `Kassza Teszt Kft.` |
A mock nem ellenőrzi a rendelésszám ismétlődését (71, 152), a fiókban nem regisztrált előtagot (202) és a fiók állapotát, és e-mailt sem küld. Ezeket az eseteket a `failNext()` második paraméterében adott `SzamlazzError`-ral szimuláld.
### Hiba, de a számla elkészült [#hiba-de-a-számla-elkészült]
A `failNext()` a bizonylat létrehozása előtt dob, így a hibára futott hívás a mockban nem hoz létre számlát. Azt az esetet, amikor a számla a hiba ellenére elkészült (például `[56]`), egy csomagoló klienssel teszteled, amely előbb kiállítja a számlát, és csak utána dob:
```ts title="lib/szamlazas.test.ts"
import { type Kassza, SzamlazzError } from 'kassza'
import { createMockKassza } from 'kassza/testing'
import { expect, test } from 'vitest'
import { szamlazRendelest } from './szamlazas'
test('részleges siker után nem állít ki új számlát', async () => {
const mock = createMockKassza()
const kassza: Kassza = {
...mock,
invoices: {
...mock.invoices,
create: async (input) => {
await mock.invoices.create(input)
throw new SzamlazzError('[56] Az értesítő e-mail nem ment ki.', {
category: 'partial_success',
code: 56,
})
},
},
}
const szamlaszam = await szamlazRendelest(kassza, {
id: 1002,
vevo: { name: 'Nagy Péter', zip: '1111', city: 'Budapest', address: 'Fő utca 1.' },
vegosszeg: 12_700,
})
expect(mock.invoiceRecords.has(szamlaszam)).toBe(true)
expect(mock.invoiceRecords.size).toBe(1)
})
```
# Hamis Számla Agent
URL: https://www.kasszajs.hu/docs/kiegeszitok/hamis-agent
> createFakeAgentFetch a kassza/testing modulból. Egy fetch, amely a valódi kérés-XML-t dolgozza fel, sorszámoz, a Számlázz.hu szabályai szerint hibát ad, és elveszett választ, karbantartást vagy részleges sikert szimulál.
A [mock kliens](/docs/kiegeszitok/teszteles) a `Kassza` felületét cseréli le. Ez gyors, de a kassza belsejét kihagyja: nem készül XML, nincs válaszfeldolgozás, és nem fut az újrapróbálás. A `createFakeAgentFetch()` eggyel lejjebb, a `fetch` szintjén dolgozik: a valódi `createKassza()` klienst kapcsolod rá, így a teljes út lefut. A kérés-XML összeállításától a multipart küldésen és a válasz értelmezésén át a `createOnce` visszakereséséig minden úgy történik, mint élesben, csak a Számlázz.hu helyett egy memóriában futó hamis Agent válaszol.
```ts
import { createKassza } from 'kassza'
import { createFakeAgentFetch } from 'kassza/testing'
const agent = createFakeAgentFetch()
const kassza = createKassza({ agentKey: 'teszt-kulcs', fetch: agent.fetch, retryDelayMs: 0 })
const szamla = await kassza.invoices.create({
buyer: { name: 'Vevő Kft.', zip: '1111', city: 'Budapest', address: 'Fő utca 1.' },
items: [{ name: 'Tanácsadás', netUnitPrice: 20_000, vat: 27 }],
})
console.log(szamla.number, agent.invoices.size, agent.requests.at(-1)?.action)
```
A hamis Agent ugyanazokat a válaszformátumokat adja, mint a Számlázz.hu. A kassza saját tesztjei a hivatalos XSD-kkel ellenőrzik is ezeket: számla XML, számlaválasz, nyugtaválasz, kiküldés.
## Mit tud? [#mit-tud]
| Terület | Műveletek |
| --- | --- |
| Számla | Kiállítás (számla, díjbekérő, előleg-, vég-, helyesbítő számla, szállítólevél, e-számla, előnézet), sztornó, befizetés, PDF, XML lekérés, díjbekérő törlése |
| Nyugta | Kiállítás, sztornó, lekérdezés nyugtaszám vagy rendelésszám alapján, kiküldés |
| Egyéb | Adószám-lekérdezés, megbízói csatlakozás |
A sorszámok: számla `KASSZA-2026-1` (vagy a megadott előtaggal), e-számla `E-…`, díjbekérő `D-…`, szállítólevél `SZL-…`, nyugta `{előtag}-2026-1`. A sztornó az eredeti sorozatában kap számot.
A Számlázz.hu szabályai közül ezeket alkalmazza:
- tételösszegek: 259, 260, 261, forintos nyugtán 261, 363, 364 és 365; ismeretlen áfakulcs: 395,
- nyugta: hívásazonosító-ismétlés 338, előtagformátum 337, számlán már használt előtag 336, nem engedélyezett előtag 524, eltérő kifizetési összeg 340,
- számla: nem regisztrált előtag 202; tiltott rendelésszám-ismétlésnél 152, de az azonos kérést két napon belül a meglévő számlával válaszolja meg, ahogy a Számlázz.hu,
- nem létező bizonylat: 7, 339, díjbekérőnél 335,
- díjbekérő sztornójára az eredeti díjbekérő számát adja vissza (a kassza ezt `validation` hibává alakítja), sztornó számla és már sztornózott bizonylat sztornójára hibát ad,
- hiányzó XML 53, hibás XML 57, rossz hitelesítés 3.
## Hibák szimulálása [#hibák-szimulálása]
```ts
agent.fail('ghostSuccess', { action: 'createInvoice' })
const eredmeny = await kassza.invoices.createOnce({ orderNumber: 'WEB-1', buyer, items })
expect(eredmeny.created).toBe(true)
expect(agent.invoices.size).toBe(1)
```
| Hiba | Mit szimulál |
| --- | --- |
| `ghostSuccess` | A kérés feldolgozódik, a bizonylat elkészül, de a válasz elveszik (időtúllépés). |
| `timeout`, `networkError` | A kérés meg sem érkezik; időtúllépés vagy hálózati hiba. |
| `serverError` | HTTP 500 válasz HTML-lel. |
| `maintenance` | Karbantartás (1). |
| `partialSuccess` | A bizonylat elkészül, de 56-os hiba jön (az értesítő e-mail nem ment ki). |
| `testAccountLimit` | A tesztfiók korlátja (167). |
| `duplicateCallId`, `duplicateOrderNumber` | 338, illetve 152 a kérés rendelésszámával. |
| `{ code, message, afterSuccess }` | Tetszőleges hibakód, a művelet előtt vagy (afterSuccess) után. |
A `fail(hiba, { action, times })` alapból a következő egy kérésre hat; az `action` egy műveletre szűkít (például `createReceipt`), a `times` többször ismétli. Kezdő hibák a `faults` opcióval is megadhatók.
## Beállítások [#beállítások]
| Opció | Alapérték | Jelentés |
| --- | --- | --- |
| `now` | a valódi idő | A hamis szerver órája (sorszám éve, nyugta kelte, 2 napos ablak). |
| `agentKeys`, `users` | – | Ha megadod, csak ezekkel a kulcsokkal és felhasználókkal enged be (3-as hiba). Nélkülük bármilyen hitelesítést elfogad. |
| `testAccount` | `true` | A bizonylatokon a `teszt` jelzés. |
| `invoicePrefixes`, `defaultInvoicePrefix` | bármi, `KASSZA` | A fiókban regisztrált számlaelőtagok. |
| `receiptPrefixes` | bármi | A fiókban engedélyezett nyugtaelőtagok. |
| `rejectDuplicateOrderNumbers` | `false` | A „Rendelésszám ismétlődés tiltása” beállítás, számlára és nyugtára külön is: `{ invoices: true }`. |
| `seller` | Kassza Teszt Kft. | Az eladó adatai a lekért számlán. |
| `taxpayers` | `{}` | Adószám-lekérdezés eredményei törzsszám szerint. |
| `principals` | `{}` | Megbízói fiókok: `owned` vagy `unowned`. |
| `faults` | `[]` | Kezdő hibák. |
## Állapot vizsgálata [#állapot-vizsgálata]
- `agent.invoices`, `agent.receipts`: a létrejött bizonylatok számszerint, sztornó- és törlésjelzéssel.
- `agent.requests`: minden kérés művelete, nyers XML-je és mellékletei, sorrendben. Ebből ellenőrizheted, hányszor próbálkozott a kassza, vagy mit küldött pontosan.
- `agent.reset()`: mindent kiürít, és a kezdő hibákat újra élesíti.
- `agent.acceptDelegation(adószám)`: a megbízó elfogadja a megbízotti kapcsolatot ([megbízotti számlázás](/docs/megbizott-szamlazas) tesztjéhez).
## Mock vagy hamis Agent? [#mock-vagy-hamis-agent]
| | `createMockKassza()` | `createFakeAgentFetch()` |
| --- | --- | --- |
| Mit cserél le | A `Kassza` felületet | Csak a `fetch`-et |
| Fut a kassza XML-építése és válaszfeldolgozása | Nem | Igen |
| Újrapróbálás, `createOnce` visszakeresés, próbálkozásnapló | Részben | Teljesen, valódi kódon |
| Szerveroldali szabályok (sorszám, 338, 152, kerekítés) | Részben | Igen |
| Sebesség | Leggyorsabb | Gyors (memóriában fut) |
Egyszerű üzleti logikához elég a mock. A webhook-kezelést, az újrapróbálást és a hibatűrést hamis Agenttel teszteld.
## Kapcsolódó [#kapcsolódó]
A mock kliens és a Vitest példák.
Amit a hamis Agenttel érdemes tesztelni.
# Validátorok
URL: https://www.kasszajs.hu/docs/kiegeszitok/validatorok
> A kassza/validators modul. Magyar adószám CDV ellenőrzéssel, bankszámla, IBAN, EU adószám, e-mail, irányítószám, Agent kulcs és cím feldolgozása űrlap-validációhoz.
A Számlázz.hu sok hibát csak a számla elküldésekor jelez, és addigra a vevő már rég elhagyta az űrlapot. A `kassza/validators` függvényeivel a pénztár űrlapján, még a rendelés előtt kiszűrheted az elgépelt adószámot, bankszámlát vagy címet. A modul hálózatot nem hív, böngészőben is futtatható, és a függvényei nem dobnak hibát: érvénytelen vagy nem szöveges bemenetre `false`-t vagy `undefined`-ot adnak.
## Az összes export [#az-összes-export]
### Adószám [#adószám]
| Export | Eredmény | Mit ellenőriz? |
| --- | --- | --- |
| `parseHungarianTaxNumber(value)` | `HungarianTaxNumber \| undefined` | 11 számjegy kötőjellel, szóközzel vagy elválasztó nélkül. A törzsszám ellenőrző számjegye (CDV), az áfakód (1–5) és a megyekód. |
| `isValidHungarianTaxNumber(value)` | `boolean` | Ugyanaz, mint a `parseHungarianTaxNumber`, igen/nem válasszal. |
| `isValidHungarianTaxpayerId(value)` | `boolean` | A 8 jegyű törzsszám és az ellenőrző számjegye. |
| `isValidHungarianGroupTaxNumber(value)` | `boolean` | Érvényes adószám `5`-ös áfakóddal (csoportazonosító szám). |
| `isHungarianVatGroupMemberTaxNumber(value)` | `boolean` | Érvényes adószám `4`-es áfakóddal (csoporttag adószáma). |
| `HUNGARIAN_TAX_COUNTY_CODES` | `readonly string[]` | Az elfogadott 43 megyekód: `02`–`20`, `22`–`44` és `51`. |
A `HungarianTaxNumber` mezői: `taxpayerId` (törzsszám), `vatCode` (áfakód), `countyCode` (megyekód) és `formatted`, ami mindig `12345676-2-41` alakú.
### Bankszámla és IBAN [#bankszámla-és-iban]
| Export | Eredmény | Mit ellenőriz? |
| --- | --- | --- |
| `parseHungarianBankAccount(value)` | `HungarianBankAccount \| undefined` | 16 vagy 24 számjegy, szóközzel vagy kötőjellel is. Az első 8 és a többi számjegy ellenőrző számjegye. |
| `isValidHungarianBankAccount(value)` | `boolean` | Ugyanaz igen/nem válasszal. |
| `formatHungarianBankAccount(value)` | `string \| undefined` | Érvényes számlaszámot 8-as blokkokra tagol: `11773016-11111018`. |
| `isValidHungarianIban(value)` | `boolean` | `HU` előtag, 28 karakter, mod 97 ellenőrzés. Szóközt és kisbetűt is elfogad. |
A `HungarianBankAccount` mezői: `digits` (csak a számjegyek), `bankCode` (első 3 jegy), `branchCode` (a következő 4 jegy) és `formatted`.
### EU adószám, e-mail, Agent kulcs [#eu-adószám-e-mail-agent-kulcs]
| Export | Eredmény | Mit ellenőriz? |
| --- | --- | --- |
| `isValidEuVatNumber(value)` | `boolean` | Országkód és az adott ország formátuma. Ellenőrző összeget nem számol. |
| `normalizeEuVatNumber(value)` | `string` | Eltávolítja a szóközt, pontot és kötőjelet, és nagybetűsít: `de 123.456.789` → `DE123456789`. |
| `EU_VAT_NUMBER_PATTERNS` | `Record` | Országonként az országkód utáni rész mintája. |
| `isValidEmail(value)` | `boolean` | Szóköz nélküli `nev@domain.tld` alak, legfeljebb 254 karakter. A körülötte lévő szóközt levágja. |
| `normalizeEmail(value)` | `string` | Levágja a szóközt és kisbetűsít. |
| `isValidAgentKey(value)` | `boolean` | Nem üres, nincs benne szóköz, és nincs benne nagybetű. |
Az `EuVatCountryPrefix` a 27 tagállam kódja és az `XI` (Észak-Írország). Görögország kódja `EL`, a `GR` előtagot a validátor elutasítja.
### Cím [#cím]
| Export | Eredmény | Mit ellenőriz? |
| --- | --- | --- |
| `isValidHungarianZipCode(value)` | `boolean` | 4 számjegy 1000 és 9999 között, szövegként vagy számként. |
| `parseHungarianAddress(value)` | `HungarianAddress \| undefined` | Egysoros magyar címet bont irányítószámra, településre és címre. |
A `parseHungarianAddress` ezeket az alakokat ismeri fel:
| Bemenet | Eredmény |
| --- | --- |
| `1031 Budapest, Záhony utca 7.` | `{ zip: '1031', city: 'Budapest', address: 'Záhony utca 7.' }` |
| `6720 Szeged Kárász utca 5.` | `{ zip: '6720', city: 'Szeged', address: 'Kárász utca 5.' }` |
| `Fő utca 1., 1111 Budapest` | `{ zip: '1111', city: 'Budapest', address: 'Fő utca 1.' }` |
| `1111 Budapest, XI. kerület, Fő utca 1.` | `{ zip: '1111', city: 'Budapest', address: 'Fő utca 1.', district: 11 }` |
| `H-1011 Budapest I. ker., Fő utca 1., Magyarország` | `{ zip: '1011', city: 'Budapest', address: 'Fő utca 1.', district: 1 }` |
A budapesti kerületet római (`XI.`, `V. ker.`) és arab számmal (`13. kerület`) is felismeri, és a `district` mezőbe teszi. A `H-` vagy `HU-` előtagot és a végén álló `Magyarország` vagy `Hungary` szót elhagyja. Ha a címből nem lesz érvényes irányítószám, település és cím, `undefined`-ot ad.
## Űrlap-validáció [#űrlap-validáció]
A validátorokat a szerveren is futtasd, ne csak a böngészőben, mert a kliensoldali ellenőrzés megkerülhető. Az alábbi példa egy pénztár számlázási adatait ellenőrzi, és hiba nélküli űrlapból kész `InvoiceBuyer` objektumot ad vissza:
```ts title="lib/szamlazasi-adatok.ts"
import type { InvoiceBuyer } from 'kassza'
import {
isValidEmail,
isValidHungarianZipCode,
normalizeEmail,
parseHungarianTaxNumber,
} from 'kassza/validators'
export interface SzamlazasiUrlap {
readonly nev: string
readonly email: string
readonly iranyitoszam: string
readonly telepules: string
readonly cim: string
readonly adoszam: string
}
type Hibak = Partial>
export type Eredmeny =
| { readonly ok: true; readonly vevo: InvoiceBuyer }
| { readonly ok: false; readonly hibak: Hibak }
export function ellenorizSzamlazasiAdatok(urlap: SzamlazasiUrlap): Eredmeny {
const vanAdoszam = urlap.adoszam.trim() !== ''
const adoszam = vanAdoszam ? parseHungarianTaxNumber(urlap.adoszam) : undefined
const hibak: Hibak = {
...(urlap.nev.trim() === '' ? { nev: 'Add meg a számlázási nevet.' } : {}),
...(isValidEmail(urlap.email) ? {} : { email: 'Érvénytelen e-mail cím.' }),
...(isValidHungarianZipCode(urlap.iranyitoszam)
? {}
: { iranyitoszam: 'Az irányítószám 4 számjegyű.' }),
...(urlap.telepules.trim() === '' ? { telepules: 'Add meg a települést.' } : {}),
...(urlap.cim.trim() === '' ? { cim: 'Add meg az utcát és a házszámot.' } : {}),
...(vanAdoszam && !adoszam ? { adoszam: 'Az adószám hibás, ellenőrizd a számjegyeket.' } : {}),
}
if (Object.keys(hibak).length > 0) return { ok: false, hibak }
return {
ok: true,
vevo: {
name: urlap.nev.trim(),
email: normalizeEmail(urlap.email),
zip: urlap.iranyitoszam.trim(),
city: urlap.telepules.trim(),
address: urlap.cim.trim(),
...(adoszam ? { taxNumber: adoszam.formatted } : {}),
},
}
}
```
```ts title="lib/szamlazasi-adatok.ts"
import {
isValidEmail,
isValidHungarianZipCode,
normalizeEmail,
parseHungarianTaxNumber,
} from 'kassza/validators'
import { z } from 'zod'
export const szamlazasiAdatok = z.object({
nev: z.string().trim().min(1, { message: 'Add meg a számlázási nevet.' }),
email: z
.string()
.refine(isValidEmail, { message: 'Érvénytelen e-mail cím.' })
.transform(normalizeEmail),
iranyitoszam: z
.string()
.trim()
.refine(isValidHungarianZipCode, { message: 'Az irányítószám 4 számjegyű.' }),
telepules: z.string().trim().min(1, { message: 'Add meg a települést.' }),
cim: z.string().trim().min(1, { message: 'Add meg az utcát és a házszámot.' }),
adoszam: z
.string()
.trim()
.refine((ertek) => ertek === '' || parseHungarianTaxNumber(ertek) !== undefined, {
message: 'Az adószám hibás, ellenőrizd a számjegyeket.',
})
.transform((ertek) => parseHungarianTaxNumber(ertek)?.formatted),
})
```
Az adószámot mindig a `formatted` alakban add tovább a számlának, így a `13421739241` és a `13421739 2 41` bevitel is `13421739-2-41` lesz.
### EU-s vevő [#eu-s-vevő]
Közösségi adószámnál előbb normalizáld a bevitelt, és azt mentsd el. Ugyanez a függvény a `HU` előtagú magyar közösségi adószámot is elfogadja:
```ts
import { isValidEuVatNumber, normalizeEuVatNumber } from 'kassza/validators'
const bevitel = 'de 123.456.789'
const euAdoszam = isValidEuVatNumber(bevitel) ? normalizeEuVatNumber(bevitel) : undefined
```
Itt az `euAdoszam` értéke `DE123456789`, amit a számla `buyer.euTaxNumber` mezőjében adhatsz át.
## Amit a validátorok nem tudnak [#amit-a-validátorok-nem-tudnak]
A validátorok formátumot és ellenőrző számjegyet vizsgálnak, azt nem, hogy a szám valóban létezik-e:
- **Adószám:** egy formailag helyes adószámhoz is tartozhat megszűnt vagy nem létező cég. A NAV nyilvántartását a [`kassza.taxpayer.query()`](/docs/adoszam-lekerdezes) kérdezi le, ami a cég nevét és címét is visszaadja.
- **EU adószám:** csak az ország szerinti formátumot nézi, az ellenőrző összeget és a regisztrációt nem.
- **Bankszámla és IBAN:** a számlaszám lehet formailag helyes, de nem létező.
- **E-mail:** a cím formáját vizsgálja, azt nem, hogy a postafiók létezik-e.
Az `isValidAgentKey()` csak a kulcs formáját nézi. A nagybetűs kulcsot a `createKassza()` is
elutasítja, már létrehozáskor `configuration` hibával. Azt, hogy a Számlázz.hu elfogadja-e a
kulcsot, a `kassza.verifyCredentials()` mondja meg, lásd a
[Hitelesítés](/docs/alapok/hitelesites#a-kulcs-ellenőrzése) oldalt.
# Pénzszámítás
URL: https://www.kasszajs.hu/docs/kiegeszitok/penzszamitas
> A kassza/money modul. Tételösszegek számla és nyugta kerekítési szabályai szerint, áfabontás, kosár végösszeg, pénzügyi kerekítés és áfakulcs ellenőrzés.
A kassza a számla tételeinek nettó, áfa és bruttó értékét maga számolja ki az egységárból és az áfakulcsból. Ugyanezek a függvények a `kassza/money` modulból is elérhetők, így a kosárban, a rendelés-összesítőben vagy a visszaigazoló e-mailben pontosan azt az összeget mutathatod, ami a számlára kerül. A modul nem hív hálózatot és nem kell hozzá Agent kulcs, ezért böngészőben is futtatható.
## Az összes export [#az-összes-export]
| Export | Mire való? |
| --- | --- |
| `calculateInvoiceItem(input, currency?)` | Egy számlatétel összegei a számla kerekítési szabályai szerint |
| `calculateReceiptItem(input, currency?)` | Egy nyugtatétel összegei a nyugta kerekítési szabályai szerint |
| `calculateItemAmounts(input, { kind, currency? })` | Ugyanez, a bizonylat fajtája (`'invoice'` vagy `'receipt'`) paraméterben |
| `summarizeItems(items)` | Végösszeg és áfakulcsonkénti bontás a kiszámolt tételekből |
| `roundMoney(value, decimals?)` | Kereskedelmi kerekítés, alapból egészre |
| `addMoney(...values)` | Összeadás lebegőpontos zaj nélkül |
| `decimalPlaces(value)` | Egy szám tizedesjegyeinek száma |
| `isVatRate(value)` | Típusőr: a Számlázz.hu által elfogadott áfakulcs-e |
| `vatPercentage(rate)` | Az áfakulcs százalékban, a szöveges kódoknál `0` |
| `formatVatRate(rate)` | Az áfakulcs szövegként, ahogy az XML-be kerül |
| `NUMERIC_VAT_RATES` | A 36 számmal megadható áfakulcs |
| `SPECIAL_VAT_CODES` | A 16 szöveges áfakód, például `AAM`, `TAM`, `EUFAD37` |
| `isHuf(currency)` | Forintnak számít-e a megadott pénznem |
A típusok is innen importálhatók: `ItemPriceInput`, `ItemAmounts`, `DocumentKind`, `DocumentTotals`, `VatBreakdown`, `VatRate`, `NumericVatRate` és `SpecialVatCode`.
## Egy tétel kiszámolása [#egy-tétel-kiszámolása]
```ts
import { calculateInvoiceItem, calculateReceiptItem } from 'kassza/money'
const b2b = calculateInvoiceItem({ quantity: 3, netUnitPrice: 500, vat: 27 })
const b2c = calculateInvoiceItem({ quantity: 3, grossUnitPrice: 500, vat: 27 })
const euro = calculateInvoiceItem({ quantity: 7, netUnitPrice: 12.35, vat: 27 }, 'EUR')
const nyugta = calculateReceiptItem({ grossUnitPrice: 1_000, vat: 27 })
```
| Változó | `netUnitPrice` | `netAmount` | `vatAmount` | `grossAmount` |
| --- | --- | --- | --- | --- |
| `b2b` | 500 | 1 500 | 405 | 1 905 |
| `b2c` | 393,67 | 1 181 | 319 | 1 500 |
| `euro` | 12,35 | 86,45 | 23,34 | 109,79 |
| `nyugta` | 787,4 | 787,4 | 212,6 | 1 000 |
A bemenet (`ItemPriceInput`) mezői:
| Mező | Leírás |
| --- | --- |
| `vat` | Áfakulcs, számmal (`27`) vagy kóddal (`'AAM'`). Kötelező. |
| `quantity` | Mennyiség, alapból `1`. Nem lehet `0`, negatív lehet. |
| `netUnitPrice` | Nettó egységár, nettó alapú számításhoz. |
| `grossUnitPrice` | Bruttó egységár, bruttó alapú számításhoz. A kettő közül pontosan egyet adj meg. |
| `netAmount`, `vatAmount`, `grossAmount` | Saját összegek. Ha bármelyiket megadod, mindhármat meg kell adnod, és a függvény nem számol. |
Az eredmény (`ItemAmounts`) a `quantity`, `vat`, `netUnitPrice`, `netAmount`, `vatAmount` és `grossAmount` mezőt tartalmazza. Bruttó alapú számításnál a `netUnitPrice` a nettó értékből visszaszámolt egységár.
A második paraméter a pénznem. Ha elhagyod, vagy az `isHuf()` szerint forint, a forintos szabályok érvényesek, minden más pénznemnél a devizásak:
| Bizonylat | Nettó érték | Áfa érték | Bruttó érték |
| --- | --- | --- | --- |
| Forintos számla | egész | egész | egész |
| Forintos nyugta | 2 tizedes | 2 tizedes | egész |
| Devizás számla vagy nyugta | 2 tizedes | 2 tizedes | 2 tizedes |
A lépések sorrendjét és a bruttó alapú visszaszámolást a [Kerekítés](/docs/szamla-letrehozas/beallitasok-es-szabalyok/kerekites) oldal, a nyugta szabályait a [Tételösszegek](/docs/nyugta-letrehozas/beallitasok/tetelosszegek) oldal írja le.
Nulla mennyiségnél, ismeretlen áfakulcsnál, nem véges számnál, két egységárnál, egységár
hiányában és hiányos saját összegeknél a függvények `validation` kategóriájú `SzamlazzError`-t
dobnak magyar üzenettel, például: `A netUnitPrice és a grossUnitPrice közül csak az egyiket add
meg.` A számla kiállításakor ugyanez a hiba jön, a tétel sorszámával és nevével kiegészítve.
## Kosár végösszege [#kosár-végösszege]
A kerekítés tételenként történik, ezért a végösszeget is a kiszámolt tételekből kell összeadni. Ezt csinálja a `summarizeItems()`:
```ts
import { calculateInvoiceItem, summarizeItems } from 'kassza/money'
const kosar = [
{ name: 'Társasjáték', quantity: 2, grossUnitPrice: 5_990, vat: 27 },
{ name: 'Szakácskönyv', quantity: 1, grossUnitPrice: 3_500, vat: 5 },
{ name: 'Házhoz szállítás', quantity: 1, grossUnitPrice: 1_490, vat: 27 },
]
const tetelek = kosar.map((sor) => calculateInvoiceItem(sor))
const { netAmount, vatAmount, grossAmount, byVat } = summarizeItems(tetelek)
```
| Áfakulcs | Nettó | Áfa | Bruttó |
| --- | --- | --- | --- |
| 27% | 10 606 Ft | 2 864 Ft | 13 470 Ft |
| 5% | 3 333 Ft | 167 Ft | 3 500 Ft |
| **Összesen** | **13 939 Ft** | **3 031 Ft** | **16 970 Ft** |
A `byVat` tömb az áfakulcsokat a tételekben való első előfordulásuk sorrendjében tartalmazza. Az összegeket a függvény 6 tizedesjegyre kerekíti, így a devizás tételek összeadásakor sem marad lebegőpontos zaj a végén.
Ha ugyanezekkel a tételekkel állítod ki a számlát, a `kassza.invoices.create()` válaszában a `netTotal` 13 939, a `grossTotal` 16 970 lesz, vagyis a kosárban mutatott összeg és a számla fillérre egyezik:
```ts
const szamla = await kassza.invoices.create({
orderNumber: 'REND-2042',
buyer: { name: 'Nagy Péter', zip: '1111', city: 'Budapest', address: 'Fő utca 1.' },
items: kosar,
})
```
Ha a bruttó végösszegből vagy a nettó összegből egyben számolod az áfát, a tételenkénti kerekítés
miatt egy-két forint eltérés lehet a számlához képest. Mindig a `summarizeItems()` eredményét
mutasd.
## Kerekítés és összeadás [#kerekítés-és-összeadás]
A JavaScript beépített kerekítése pénzösszegeknél meglepetéseket okoz. A `roundMoney()` kereskedelmi kerekítést használ (a fél érték nulláról elfelé kerekedik), és a lebegőpontos pontatlanságot 15 értékes jegyre normalizálja. Az `addMoney()` összead, és az eredményt a bemenetek közül a legtöbb tizedesjegyre kerekíti.
```ts
import { addMoney, decimalPlaces, roundMoney } from 'kassza/money'
const ketTizedes = roundMoney(1.005, 2)
const negativ = roundMoney(-2.5)
const osszeg = addMoney(0.1, 0.2)
const tizedesek = decimalPlaces(12.35)
```
| Változó | kassza | Beépített megoldással |
| --- | --- | --- |
| `ketTizedes` | `1.01` | `Math.round(1.005 * 100) / 100` eredménye `1` |
| `negativ` | `-3` | `Math.round(-2.5)` eredménye `-2` |
| `osszeg` | `0.3` | `0.1 + 0.2` eredménye `0.30000000000000004` |
| `tizedesek` | `2` | – |
A `roundMoney()` második paramétere a tizedesjegyek száma, alapból `0`. `NaN` vagy végtelen értékre `RangeError`-t dob.
## Áfakulcsok és pénznem [#áfakulcsok-és-pénznem]
Az `isVatRate()` típusőr megmondja, hogy egy érték a Számlázz.hu által elfogadott áfakulcs-e: szám a `NUMERIC_VAT_RATES` listából, vagy szöveg a `SPECIAL_VAT_CODES` listából. Számot tartalmazó szövegre (`'27'`) `false`-t ad, ezért adatbázisból vagy CSV-ből olvasott értéket előbb alakíts át:
```ts
import { isVatRate, type VatRate } from 'kassza/money'
export function afakulcs(ertek: string): VatRate {
const szoveg = ertek.trim()
const szam = Number(szoveg)
const jelolt = szoveg !== '' && Number.isFinite(szam) ? szam : szoveg
if (!isVatRate(jelolt)) throw new Error(`Ismeretlen áfakulcs: ${ertek}`)
return jelolt
}
```
Ezzel a `'27'` és az `' 5.5 '` számmá, az `'AAM'` kóddá alakul, a `'28'` pedig hibát dob. A kulcsok jelentését és a szöveges kódokat az [Áfakulcsok](/docs/szamla-letrehozas/beallitasok-es-szabalyok/afakulcsok) oldal sorolja fel.
A `vatPercentage()` a számmal megadott kulcsot adja vissza, a szöveges kódoknál `0`-t, és a kassza ezzel a százalékkal számolja a tétel áfáját. Az `isHuf()` akkor ad `true`-t, ha a pénznem hiányzik, vagy a szóközök levágása után `HUF`, `huf`, `Ft`, `FT` vagy `ft`. Más írásmódot, például `Huf`-ot, devizának vesz.
# PDF tárhely
URL: https://www.kasszajs.hu/docs/kiegeszitok/pdf-tarhely
> A kassza/storage modul. A számla és a nyugta PDF-jének mentése S3-ba, Cloudflare R2-be, Vercel Blobba, UploadThingre, Supabase-be vagy lemezre, dátum szerinti kulcsokkal és lejáró letöltési linkkel.
Számla kiállításakor a kassza alapból letölti a PDF-et is, és a válasz `pdf` mezőjében `Uint8Array`-ként adja vissza. Ha ezt elmented egy tárhelyre, a vevőnek letöltési linket adhatsz, és nem kell minden letöltésnél a Számlázz.hu-t hívnod. A `kassza/storage` modul ehhez ad egy közös felületet és kész adaptereket. Egyiknek sincs függősége: a szolgáltató SDK-ját, ha kell, te adod át.
```ts title="lib/tarhely.ts"
import { s3FetchStorage } from 'kassza/storage'
export const tarhely = s3FetchStorage({
bucket: 'szamlak',
region: 'auto',
endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,
accessKeyId: process.env.R2_ACCESS_KEY_ID ?? '',
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY ?? '',
})
```
```ts
import { invoicePdfKey, storePdf } from 'kassza/storage'
import { tarhely } from '@/lib/tarhely'
const szamla = await kassza.invoices.create(adatok)
if (szamla.pdf) {
const fajl = await storePdf(tarhely, invoicePdfKey({ number: szamla.number }), szamla.pdf)
await db.rendeles.update({ where: { id: rendelesId }, data: { szamlaPdfKulcs: fajl.key } })
}
```
A kulcs ebben a példában `szamlak/2026/09/E-WEB-2026-12.pdf` alakú lesz. Az adatbázisba a kulcsot mentsd, ne a PDF-et base64-ként és ne egy lejáró URL-t.
## A storePdf [#a-storepdf]
A `storePdf(storage, key, pdf)` ellenőrzi, hogy a tartalom valóban PDF-e (a `%PDF` fejléccel kezdődik), majd `application/pdf` típussal elmenti. Ha a tartalom nem PDF, `StorageError`-t dob, és nem ír semmit. Az eredmény egy `StoredFile`:
| Mező | Típus | Leírás |
| --- | --- | --- |
| `key` | `string` | Az általad megadott kulcs, az adapter `prefix` opciója nélkül. |
| `url` | `string \| undefined` | Letöltési cím, ha az adapter a mentéskor meg tudja adni. |
| `size` | `number` | A fájl mérete bájtban. |
| `contentType` | `string` | `application/pdf` |
Ha a számlát `downloadPdf: false` beállítással állítottad ki, vagy a mentés nem sikerült, a PDF-et később az [`invoices.getPdf()`](/docs/bizonylat-pdf) metódussal kérheted le.
## Kulcsok [#kulcsok]
Az `invoicePdfKey()` és a `receiptPdfKey()` év és hónap szerinti mappába rendezett, ékezet nélküli kulcsot készít a bizonylatszámból:
```ts
import { invoicePdfKey, receiptPdfKey } from 'kassza/storage'
const szamla = invoicePdfKey({ number: 'E-WEB-2026-12' })
const dijbekero = invoicePdfKey({ number: 'D-WEB-2026-3', type: 'proforma', date: '2026-01-31' })
const nyugta = receiptPdfKey({ number: 'NYGT-2026-45', date: '2026-09-17' })
const ugyfelenkent = invoicePdfKey({ number: 'E-WEB-2026-12', prefix: 'ugyfelek/Árvíztűrő Kft', date: '2026-09-17' })
```
| Változó | Kulcs |
| --- | --- |
| `szamla` | `szamlak/2026/09/E-WEB-2026-12.pdf` (a mai nap szerint) |
| `dijbekero` | `szamlak/dijbekero/2026/01/D-WEB-2026-3.pdf` |
| `nyugta` | `nyugtak/2026/09/NYGT-2026-45.pdf` |
| `ugyfelenkent` | `ugyfelek/Arvizturo-Kft/2026/09/E-WEB-2026-12.pdf` |
| Paraméter | Alapérték | Leírás |
| --- | --- | --- |
| `number` | – | A bizonylatszám, ebből lesz a fájlnév. Kötelező. |
| `type` | `'invoice'` | Csak az `invoicePdfKey`-nél. Almappát ad: `proforma` → `dijbekero`, `advance` → `elolegszamla`, `final` → `vegszamla`, `corrective` → `helyesbito`, `storno` → `sztorno`, `deliveryNote` → `szallitolevel`. |
| `prefix` | `szamlak` vagy `nyugtak` | Az első mappa, perjellel tagolva több szint is lehet. |
| `date` | ma, budapesti idő szerint | `Date` vagy `YYYY-MM-DD`. Ebből lesz az év és a hónap. |
Minden szakaszból eltűnnek az ékezetek, és ami nem betű, szám, pont, aláhúzás vagy kötőjel, az kötőjel lesz. Ugyanezt a `sanitizeKeySegment()` függvény külön is elvégzi.
Dátum nélkül a kulcs a mentés napjára mutat. Ha később ugyanazzal a hívással újra kiszámolod,
hónapfordulón más kulcsot kaphatsz. A mentéskor kapott `key`-t tárold az adatbázisban.
Az adapterek minden kulcsot ellenőriznek: nem lehet üres vagy 1024 karakternél hosszabb, nem kezdődhet perjellel, és nem lehet benne visszaperjel, vezérlőkarakter, üres, `.` vagy `..` szakasz. Hibás kulcsnál `StorageError` jön `operation: 'key'` értékkel. Saját adapterben ugyanezt az `assertStorageKey()` végzi.
Minden adapternek van `prefix` opciója. Ez a tárhelyen a kulcs elé kerül, például `prefix: 'eles'` mellett a fájl az `eles/szamlak/2026/09/…` útvonalra kerül, a `StoredFile.key` viszont prefix nélkül marad. Így ugyanazzal a kulccsal dolgozhatsz fejlesztői és éles tárhelyen is.
## Adapterek [#adapterek]
Minden adapter tud menteni (`put`). A többi művelet adapterenként eltér:
| Adapter | Import | `get` | `delete` | `getUrl` lejárat nélkül | `getUrl` lejáró linkkel |
| --- | --- | --- | --- | --- | --- |
| `s3FetchStorage` | `kassza/storage` | igen | igen | `publicBaseUrl` esetén | aláírt URL, legfeljebb 7 nap |
| `s3Storage` | `kassza/storage` | `GetObjectCommand`-dal | `DeleteObjectCommand`-dal | `publicBaseUrl` esetén | `getSignedUrl`-lel, legfeljebb 7 nap |
| `r2BindingStorage` | `kassza/storage` | igen | igen | `publicBaseUrl` esetén | nem |
| `vercelBlobStorage` | `kassza/storage` | nem | `del`-lel | `head`-del | nem |
| `uploadthingStorage` | `kassza/storage` | nem | igen | nem, mindig aláírt | aláírt URL, legfeljebb 7 nap |
| `supabaseStorage` | `kassza/storage` | igen | igen | `public: true` esetén | aláírt URL, legfeljebb 1 év |
| `fsStorage` | `kassza/storage/fs` | igen | igen | `publicBaseUrl` esetén | nem |
| `memoryStorage` | `kassza/storage` | igen | igen | mindig | nem |
Ahol van aláírt URL, a lejárat alapból 1 óra, és a megengedettnél hosszabb lejárat `StorageError`-t ad. Az R2 binding, a Vercel Blob és a fájlrendszer adapternél már az `expiresInSeconds` megadása is `StorageError`-t ad.
Aláírt S3 kéréseket küld a beépített `fetch`-csel, AWS SDK nélkül. Az aláíráshoz a Web Crypto API-t használja, ezért edge környezetben is fut. Amazon S3, Cloudflare R2, MinIO, Backblaze és más S3-kompatibilis tárhelyek mellett is működik.
```ts
import { s3FetchStorage } from 'kassza/storage'
export const tarhely = s3FetchStorage({
bucket: 'szamlak',
region: 'eu-central-1',
accessKeyId: process.env.AWS_ACCESS_KEY_ID ?? '',
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY ?? '',
})
```
| Opció | Leírás |
| --- | --- |
| `bucket`, `region`, `accessKeyId`, `secretAccessKey` | Kötelező. R2-nél a régió `auto`. Ha bármelyik üres, a létrehozás `configuration` hibát dob. |
| `sessionToken` | Ideiglenes AWS hitelesítéshez. |
| `endpoint` | Nem AWS tárhelynél, például `https://.r2.cloudflarestorage.com`. |
| `forcePathStyle` | Endpoint nélkül alapból `https://.s3..amazonaws.com` a cím, pontot tartalmazó bucketnél vagy `true` esetén `https://s3..amazonaws.com/`. Endpointtal alapból `/`, `false` esetén `.`. |
| `publicBaseUrl` | Nyilvános cím, például egy CDN vagy az R2 egyedi domainje. |
| `prefix`, `fetch` | Kulcselőtag és saját `fetch` implementáció. |
Ha a projektben már van `@aws-sdk/client-s3`, add át a kliensét és a parancsosztályait. A `GetObjectCommand` és a `DeleteObjectCommand` csak a `get`, a `delete` és az aláírt URL-hez kell.
```ts
import { DeleteObjectCommand, GetObjectCommand, PutObjectCommand, S3Client } from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
import { s3Storage } from 'kassza/storage'
export const tarhely = s3Storage({
client: new S3Client({ region: 'eu-central-1' }),
bucket: 'szamlak',
commands: { PutObjectCommand, GetObjectCommand, DeleteObjectCommand },
getSignedUrl,
})
```
| Opció | Leírás |
| --- | --- |
| `client`, `bucket` | Kötelező. |
| `commands` | Kötelező. `PutObjectCommand` kötelező, a másik kettő opcionális. |
| `getSignedUrl` | A `@aws-sdk/s3-request-presigner` függvénye, lejáró linkhez. |
| `publicBaseUrl`, `prefix` | Nyilvános cím és kulcselőtag. |
Cloudflare Workers alatt a bucket bindingot közvetlenül is használhatod, hozzáférési kulcs nélkül:
```ts
import { createKassza } from 'kassza'
import { invoicePdfKey, r2BindingStorage, storePdf } from 'kassza/storage'
export default {
async fetch(request: Request, env: Env) {
const kassza = createKassza({ agentKey: env.SZAMLAZZ_AGENT_KEY })
const tarhely = r2BindingStorage(env.SZAMLAK, { publicBaseUrl: 'https://szamlak.example.hu' })
const szamlaszam = 'E-WEB-2026-12'
const { pdf } = await kassza.invoices.getPdf(szamlaszam)
const fajl = await storePdf(tarhely, invoicePdfKey({ number: szamlaszam }), pdf)
return Response.json(fajl)
},
}
```
Az első paraméter a binding, a második opcionális: `publicBaseUrl` és `prefix`. Lejáró linket a binding nem tud készíteni. Ha a fájlok nem nyilvánosak, a Workerből szolgáld ki őket a `get()` eredményével.
Add át a `@vercel/blob` függvényeit. A `put` kötelező, a `del` a törléshez, a `head` a `getUrl()`-hez kell.
```ts
import { del, head, put } from '@vercel/blob'
import { vercelBlobStorage } from 'kassza/storage'
export const tarhely = vercelBlobStorage({ put, del, head, access: 'public' })
```
| Opció | Leírás |
| --- | --- |
| `access` | Kötelező, `'public'` vagy `'private'`. |
| `token` | Blob token, ha nem a `@vercel/blob` alapértelmezését használod. |
| `allowOverwrite` | Felülírhatja-e a meglévő fájlt, alapból `true`. |
| `cacheControlMaxAge` | Gyorsítótárazási idő másodpercben. |
| `prefix` | Kulcselőtag. |
Az adapter véletlen utótag nélkül ment, így a fájl útvonala pontosan a kulcs. A `StoredFile.url` a blob URL-je. Olvasni (`get`) és lejáró linket készíteni nem tud.
Add át az UploadThing szerveroldali `UTApi` példányát:
```ts
import { UTApi } from 'uploadthing/server'
import { uploadthingStorage } from 'kassza/storage'
export const tarhely = uploadthingStorage({ utapi: new UTApi() })
```
| Opció | Leírás |
| --- | --- |
| `utapi` | Kötelező. |
| `acl` | `'public-read'` vagy `'private'`. |
| `contentDisposition` | `'inline'` vagy `'attachment'`. |
| `prefix` | Kulcselőtag. |
A teljes kulcs az UploadThing `customId` azonosítója lesz, ezért előtaggal együtt legfeljebb 128 karakter lehet. A fájl neve a kulcs utolsó szakasza. A `getUrl()` mindig aláírt linket ad a `generateSignedURL` függvénnyel, ehhez uploadthing v7 kell.
Add át a Supabase klienst és a bucket nevét. A kliens szerveroldalon készüljön, olyan kulccsal, amely írhat a bucketbe.
```ts
import { createClient } from '@supabase/supabase-js'
import { supabaseStorage } from 'kassza/storage'
const supabase = createClient(process.env.SUPABASE_URL ?? '', process.env.SUPABASE_SERVICE_ROLE_KEY ?? '')
export const tarhely = supabaseStorage({ client: supabase, bucket: 'szamlak' })
```
| Opció | Leírás |
| --- | --- |
| `client`, `bucket` | Kötelező. |
| `public` | Nyilvános bucketnél `true`: ekkor a `getUrl()` lejárat nélkül a nyilvános címet adja. |
| `upsert` | Felülírhatja-e a meglévő fájlt, alapból `true`. |
| `cacheControl` | A Supabase `cacheControl` beállítása. |
| `prefix` | Kulcselőtag. |
Saját szerveren a lemezre is menthetsz. Ez az adapter külön útvonalon érhető el, mert a `node:fs` modult használja:
```ts
import { fsStorage } from 'kassza/storage/fs'
export const tarhely = fsStorage({ directory: './adatok/szamlak' })
```
| Opció | Leírás |
| --- | --- |
| `directory` | Kötelező. Relatív útvonalnál a munkakönyvtárhoz képest értendő. Hiányában a létrehozás `configuration` hibát dob. |
| `publicBaseUrl` | Ha a könyvtárat egy webszerver kiszolgálja, annak címe. Nélküle a `getUrl()` hibát dob. |
| `prefix` | Kulcselőtag. |
Az adapter létrehozza a hiányzó mappákat, és előbb ideiglenes fájlba ír, majd átnevezi, így félig megírt PDF nem marad a lemezen. Olyan kulcsot, amely a könyvtáron kívülre mutatna, szimbolikus linken keresztül sem enged. A `pathFor(key)` a fájl abszolút útvonalát adja, a `directory` a gyökérkönyvtárat.
Edge környezetben nem fut, és serverless függvényben sem érdemes használni, mert ott a fájlrendszer nem tartós.
Tesztekhez és a sandboxhoz. A fájlok a folyamat memóriájában vannak:
```ts
import { memoryStorage } from 'kassza/storage'
const tarhely = memoryStorage({ publicBaseUrl: 'https://cdn.example.hu' })
```
A `publicBaseUrl` alapból `memory://storage`, a `prefix` itt is megadható. A `files` mező a mentett fájlokat tartalmazza teljes kulcs szerint (`body` és `contentType`), a `clear()` mindet törli. A `getUrl()` nem létező fájlra `StorageError`-t dob 404-es `status` értékkel.
## Letöltési link [#letöltési-link]
A vevő a PDF-et egy saját végponton keresztül kapja meg: a végpont ellenőrzi, hogy a bejelentkezett felhasználó láthatja-e a számlát, és rövid lejáratú linkre irányít át.
```ts title="app/api/szamlak/[rendelesId]/route.ts"
import { tarhely } from '@/lib/tarhely'
export async function GET(_request: Request, { params }: { params: Promise<{ rendelesId: string }> }) {
const { rendelesId } = await params
const rendeles = await db.rendeles.findUnique({ where: { id: rendelesId } })
if (!rendeles?.szamlaPdfKulcs) return new Response('Nem található', { status: 404 })
const url = await tarhely.getUrl(rendeles.szamlaPdfKulcs, { expiresInSeconds: 300 })
return Response.redirect(url, 302)
}
```
A számlán a vevő neve, címe és a vásárolt tételek is szerepelnek. Ne tedd a PDF-eket kitalálható
kulccsal nyilvános bucketbe. Használj privát tárhelyet és lejáró linket, és a link kiadása előtt
ellenőrizd a jogosultságot.
## Hibakezelés [#hibakezelés]
A tárhely hibáit a kassza `StorageError`-ba csomagolja:
| Mező | Leírás |
| --- | --- |
| `message` | Magyar hibaüzenet, benne a szolgáltató hibájával. |
| `operation` | `'put'`, `'get'`, `'delete'`, `'getUrl'` vagy `'key'`. |
| `key` | Az érintett kulcs. |
| `status` | HTTP státusz, ha ismert. |
| `cause` | Az eredeti hiba. |
Az `isStorageError()` típusőrrel szűkítheted. A hiányzó fájl nem hiba: a `get()` ilyenkor `undefined`-ot ad. A hiányzó kötelező opció (például a `bucket` vagy a `directory`) viszont már az adapter létrehozásakor `configuration` kategóriájú `SzamlazzError`-t dob.
A számla a mentés előtt már elkészült, ezért tárhelyhiba miatt soha ne állítsd ki újra. Naplózd a hibát, és a PDF-et később a számlaszám alapján kérd le újra:
```ts
import { invoicePdfKey, isStorageError, storePdf } from 'kassza/storage'
import { tarhely } from '@/lib/tarhely'
const szamla = await kassza.invoices.create(adatok)
try {
if (szamla.pdf) await storePdf(tarhely, invoicePdfKey({ number: szamla.number }), szamla.pdf)
} catch (error) {
if (!isStorageError(error)) throw error
naplo.warn({ muvelet: error.operation, kulcs: error.key, status: error.status })
await pdfMentesKesobb(szamla.number)
}
```
A `pdfMentesKesobb` egy háttérfeladatban a `kassza.invoices.getPdf(szamlaszam)` hívással tölti le újra a PDF-et, és megismétli a mentést.
## Tesztelés [#tesztelés]
A `memoryStorage` és a [mock kliens](/docs/kiegeszitok/teszteles) együtt hálózat nélkül teszteli a teljes folyamatot. A mock minimális, de érvényes PDF-et ad vissza, amit a `storePdf` elfogad:
```ts
import { invoicePdfKey, memoryStorage, storePdf } from 'kassza/storage'
import { createMockKassza } from 'kassza/testing'
import { expect, test } from 'vitest'
test('a számla PDF-je a tárhelyre kerül', async () => {
const kassza = createMockKassza({ now: () => new Date('2026-09-17T10:00:00Z') })
const tarhely = memoryStorage()
const szamla = await kassza.invoices.create({
buyer: { name: 'Vevő Kft.', zip: '1111', city: 'Budapest', address: 'Fő utca 1.' },
items: [{ name: 'Fotózás', netUnitPrice: 85_000, vat: 27 }],
})
const kulcs = invoicePdfKey({ number: szamla.number, date: '2026-09-17' })
const fajl = await storePdf(tarhely, kulcs, szamla.pdf ?? new Uint8Array())
expect(fajl.key).toBe('szamlak/2026/09/E-KASSZA-2026-1.pdf')
expect(tarhely.files.get(fajl.key)?.contentType).toBe('application/pdf')
})
```
## Saját adapter [#saját-adapter]
Ha a tárhelyedhez nincs adapter, írd meg a `StorageAdapter` felületet. Csak a `put` kötelező, a `get`, a `delete` és a `getUrl` opcionális. A `guardStorageCall()` a szolgáltató hibáját `StorageError`-ba csomagolja:
```ts
import { Storage } from '@google-cloud/storage'
import { assertStorageKey, guardStorageCall, type StorageAdapter } from 'kassza/storage'
const bucket = new Storage().bucket('szamlak')
export const tarhely: StorageAdapter = {
async put(key, body, { contentType }) {
const file = bucket.file(assertStorageKey(key))
await guardStorageCall('Google Cloud Storage', 'put', key, () => file.save(body, { contentType }))
return { key, size: body.byteLength, contentType }
},
}
```
# Serverless és edge
URL: https://www.kasszajs.hu/docs/kiegeszitok/serverless-es-edge
> A kassza futtatása Vercelen, Cloudflare Workersen, Denón, Bunon és AWS Lambdán. Közös munkamenet-tároló, környezeti változók, időkorlát és webhook minták.
A kasszának nincs futásidejű függősége, és csak webes szabvány API-kat használ (`fetch`, `FormData`, `AbortSignal`, Web Crypto). Emiatt Node.js 22-n és újabbon, Bunon, Denón, Cloudflare Workersen és a Vercel Edge futtatókörnyezetében is ugyanaz a kód fut. Egyetlen kivétel a `kassza/storage/fs`, amely a `node:fs` modult használja.
Környezettől függetlenül három dolgot kell eldöntened:
1. **Hol él a kliens?** Folyamatonként vagy izolátumonként egyszer hozd létre, modul szinten, ne kérésenként.
2. **Hol él a munkamenet?** Ha a példányok nem osztoznak a memórián, adj meg közös `cookieStore`-t. A részleteket a [Munkamenet](/docs/alapok/munkamenet) oldal írja le.
3. **Mennyi ideig futhat egy kérés?** A kassza alapból 60 másodpercig vár egy válaszra, ami több lehet, mint amennyit a platform vagy a webhook küldője enged.
| Környezet | Agent kulcs | Munkamenet | PDF tárhely |
| --- | --- | --- | --- |
| Node.js, Bun, Deno szerveren | `process.env`, Denón `Deno.env.get()` | memória (alapértelmezett) | `fsStorage`, S3 |
| Vercel Functions | `process.env` | Upstash, ioredis, node-redis | Vercel Blob, S3, R2 |
| Vercel Edge | `process.env` | Upstash | `s3FetchStorage` |
| Cloudflare Workers | `env.SZAMLAZZ_AGENT_KEY` | Cloudflare KV | `r2BindingStorage` |
| AWS Lambda | `process.env` | ioredis, node-redis, Upstash | S3 |
Edge környezetben HTTP alapú tárolót válassz (Upstash Redis, Cloudflare KV), mert az ioredis és a node-redis TCP kapcsolattal dolgozik.
## Platformonként [#platformonként]
A klienst egy közös modulban hozd létre. A Vercel Functions példányai nem osztoznak a memórián, ezért a munkamenetet Upstash Redisben tárold:
```ts title="lib/kassza.ts"
import { Redis } from '@upstash/redis'
import { createKassza } from 'kassza'
import { upstashRedisCookieStore } from 'kassza/cookie-stores'
export const kassza = createKassza({
cookieStore: upstashRedisCookieStore(Redis.fromEnv()),
timeoutMs: 15_000,
maxAttempts: 2,
})
```
Ugyanez a modul Node.js és Edge futtatókörnyezetben is működik:
```ts title="app/api/szamlazas/allapot/route.ts"
import { kassza } from '@/lib/kassza'
export const runtime = 'edge'
export async function GET() {
const kulcsJo = await kassza.verifyCredentials()
return Response.json({ kulcsJo }, { status: kulcsJo ? 200 : 500 })
}
```
Az Agent kulcsot a projekt beállításaiban vagy a `vercel env add SZAMLAZZ_AGENT_KEY` paranccsal add meg. Soha ne adj neki `NEXT_PUBLIC_` előtagot, mert az ilyen változók a böngészőbe kerülnek.
Workers alatt a környezeti változók és a bindingok a `fetch` handler `env` paraméterében érkeznek, ezért az Agent kulcsot az `agentKey` opcióban add át. A munkamenetet KV-ban, a PDF-et R2-ben tárolhatod:
```json title="wrangler.json"
{
"name": "szamlazas",
"main": "src/index.ts",
"compatibility_date": "2026-09-01",
"kv_namespaces": [{ "binding": "KASSZA_KV", "id": "" }],
"r2_buckets": [{ "binding": "SZAMLAK", "bucket_name": "szamlak" }]
}
```
```ts title="src/index.ts"
import { createKassza } from 'kassza'
import { cloudflareKvCookieStore } from 'kassza/cookie-stores'
export default {
async fetch(request: Request, env: Env) {
const rendeles = new URL(request.url).searchParams.get('rendeles')
if (!rendeles) return new Response('Hiányzó rendelésszám', { status: 400 })
const kassza = createKassza({
agentKey: env.SZAMLAZZ_AGENT_KEY,
cookieStore: cloudflareKvCookieStore(env.KASSZA_KV),
})
const szamla = await kassza.invoices.find({ orderNumber: rendeles })
return Response.json(szamla?.header ?? null)
},
}
```
Mivel az `env` csak a kérésen belül érhető el, a kliens itt kérésenként készül. Ez nem jár hálózati hívással, és a munkamenet a KV-ban megmarad a kérések között.
A kulcsot a `wrangler secret put SZAMLAZZ_AGENT_KEY` paranccsal add meg, helyi fejlesztéshez a `.dev.vars` fájlban. Az `Env` típust a `wrangler types` parancs generálja. A kassza nem használ Node.js API-t, ezért a `nodejs_compat` flag nem kell hozzá. A KV legalább 60 másodperces lejáratot fogad el, ezt az adapter magától betartja.
Denóban az npm csomagot `npm:` előtaggal importálod. A kulcsot a `Deno.env.get()` adja, ezt az `agentKey` opcióban add át:
```ts title="main.ts"
import { createKassza } from 'npm:kassza'
import { ipnOkResponse, readIpnNotification } from 'npm:kassza/ipn'
const kassza = createKassza({ agentKey: Deno.env.get('SZAMLAZZ_AGENT_KEY') })
Deno.serve(async (request) => {
const url = new URL(request.url)
if (request.method === 'POST' && url.pathname === '/szamlazz/ipn') {
const ertesites = await readIpnNotification(request)
if (ertesites.isFullyPaid) await rendelesFizetve(ertesites.invoiceNumber)
return ipnOkResponse()
}
const rendeles = url.searchParams.get('rendeles')
if (!rendeles) return new Response('Hiányzó rendelésszám', { status: 400 })
const szamla = await kassza.invoices.find({ orderNumber: rendeles })
return Response.json(szamla?.header ?? null)
})
```
```bash
deno run --allow-net --allow-env main.ts
```
Hosszan futó Deno szerveren az alapértelmezett memóriás munkamenet elég. Ha több példányon futsz, használj közös tárolót, például Upstash Redist.
Bun a `.env` fájlt magától betölti, a kassza pedig a `SZAMLAZZ_AGENT_KEY` változót a `process.env`-ből olvassa, így a `createKassza()` paraméter nélkül is működik:
```ts title="server.ts"
import { createKassza } from 'kassza'
import { ipnOkResponse, readIpnNotification } from 'kassza/ipn'
const kassza = createKassza()
Bun.serve({
port: 3000,
async fetch(request) {
const url = new URL(request.url)
if (request.method === 'POST' && url.pathname === '/szamlazz/ipn') {
const ertesites = await readIpnNotification(request)
if (ertesites.isFullyPaid) await rendelesFizetve(ertesites.invoiceNumber)
return ipnOkResponse()
}
const rendeles = url.searchParams.get('rendeles')
if (!rendeles) return new Response('Hiányzó rendelésszám', { status: 400 })
const szamla = await kassza.invoices.find({ orderNumber: rendeles })
return Response.json(szamla?.header ?? null)
},
})
```
Egy Bun folyamat hosszan fut, ezért a memóriás munkamenet itt is elég.
Node.js 22-es vagy újabb futtatókörnyezetet válassz. A modul szinten létrehozott kliens a meleg indítások között megmarad, a hideg indítás viszont új memóriát kap, ezért a munkamenetet Redisben tárold. Az alábbi függvény egy SQS sorból dolgozza fel a kiállítandó számlákat:
```ts title="handler.ts"
import Redis from 'ioredis'
import { createKassza } from 'kassza'
import { ioredisCookieStore } from 'kassza/cookie-stores'
import { szamlazRendelest } from './szamlazas'
const kassza = createKassza({
cookieStore: ioredisCookieStore(new Redis(process.env.REDIS_URL)),
timeoutMs: 15_000,
})
interface SqsEsemeny {
readonly Records: readonly { readonly body: string }[]
}
export async function handler(esemeny: SqsEsemeny) {
for (const uzenet of esemeny.Records) {
await szamlazRendelest(kassza, JSON.parse(uzenet.body))
}
}
```
A `szamlazRendelest` a [Tesztelés](/docs/kiegeszitok/teszteles#a-kliens-injektálása) oldal idempotens függvénye: először rendelésszám alapján keres, így ha egy üzenet hiba miatt újra megérkezik, nem készül második számla. A Lambda időkorlátját állítsd nagyobbra, mint amennyi ideig a kassza egy hívásnál várhat, lásd lent.
## Környezeti változók [#környezeti-változók]
- A kassza a `SZAMLAZZ_AGENT_KEY` változót a `process.env`-ből olvassa. Ahol ez nincs (Cloudflare Workers, Deno), add át a kulcsot az `agentKey` opcióban.
- A `createKassza()` már létrehozáskor `configuration` hibát dob, ha nincs kulcs, vagy ha a kulcs nagybetűt tartalmaz. Ha a klienst modul szinten hozod létre, a hiba már az első importnál jelentkezik, ezért a változót minden környezetben állítsd be, ahol a kód fut.
- Fejlesztéshez és az előnézeti környezetekhez Számlázz.hu tesztfiók kulcsát használd, az éles kulcs csak az éles környezetbe kerüljön.
- Telepítés után a `verifyCredentials()` megmondja, hogy a kulcs jó-e, bizonylat létrehozása nélkül. Hibás kulcsnál `false`, fiókproblémánál hibát dob.
## Időkorlát [#időkorlát]
A serverless platformok és a webhookot küldő szolgáltatók is korlátozzák, meddig futhat egy kérés. Vercelen ezt a route `maxDuration` beállítása, Lambdán a függvény időkorlátja adja meg. A kassza oldalán két beállítás számít:
- `timeoutMs`: egy próbálkozás időkorlátja, alapból 60 000 ms. Túllépésnél `timeout` kategóriájú `SzamlazzError` jön.
- `maxAttempts`: a biztonságosan ismételhető műveletek (lekérdezések, befizetések felülírása, `callId`-val védett nyugták) próbálkozásainak száma hálózati hiba, időtúllépés vagy karbantartás esetén, alapból 3. Két próbálkozás között 1, majd 2 másodperc a várakozás.
Egy lekérdezés így legrosszabb esetben `maxAttempts × timeoutMs` plusz a várakozások ideig tarthat. A számlakészítést a kassza soha nem ismétli, az legfeljebb egy `timeoutMs`-ig tart.
| Beállítás | `invoices.create` legfeljebb | `invoices.find` legfeljebb |
| --- | --- | --- |
| Alapértelmezés (`timeoutMs: 60_000`, `maxAttempts: 3`) | 60 s | 183 s |
| `timeoutMs: 15_000`, `maxAttempts: 2` | 15 s | 31 s |
Ha egy kérésnek összesen van határideje, adj át egy közös `AbortSignal`-t minden hívásnak. A signal a próbálkozásokat és a köztük lévő várakozást is megszakítja.
Ha a saját `AbortSignal`-od szakítja meg a hívást, a kassza a signal okát dobja tovább, ami
`AbortSignal.timeout()` esetén egy `TimeoutError` nevű `DOMException`. Ez ugyanolyan
bizonytalan kimenet, mint a `timeout` kategória: a számla elkészülhetett, ezért kezeld ugyanúgy.
## Webhook minták [#webhook-minták]
### Fizetés után számla [#fizetés-után-számla]
A fizetési szolgáltatók (Stripe, Barion, SimplePay és társaik) ugyanazt az eseményt többször is elküldhetik, és újraküldik, ha nem kapnak időben sikeres választ. Ezt kihasználva a webhook bizonytalan kimenetnél nem próbálkozik tovább, hanem hibakóddal válaszol, és a következő kézbesítés az elején rendelésszám alapján megtalálja a közben elkészült számlát:
```ts title="app/api/fizetes/webhook/route.ts"
import { isSzamlazzError } from 'kassza'
import { kassza } from '@/lib/kassza'
const BIZONYTALAN = ['network', 'timeout', 'partial_success', 'duplicate']
function bizonytalan(error: unknown): boolean {
if (isSzamlazzError(error)) return BIZONYTALAN.includes(error.category)
return error instanceof DOMException && error.name === 'TimeoutError'
}
export async function POST(request: Request) {
const esemeny = await ellenorzottEsemeny(request)
const orderNumber = `REND-${esemeny.rendelesId}`
const signal = AbortSignal.timeout(8_000)
try {
const meglevo = await kassza.invoices.find({ orderNumber }, {}, { signal })
if (meglevo) return Response.json({ szamla: meglevo.header.number })
const szamla = await kassza.invoices.create(szamlaAdatok(esemeny, orderNumber), { signal })
return Response.json({ szamla: szamla.number })
} catch (error) {
if (bizonytalan(error)) return new Response('Próbáld újra később', { status: 503 })
throw error
}
}
```
- Az `ellenorzottEsemeny` a szolgáltató aláírását ellenőrzi, a `szamlaAdatok` a rendelésből állítja össze a számlát.
- A rendelésszám a rendelés azonosítójából készül, így minden kézbesítés ugyanazt a számlát keresi.
- A `find` az első lépés, ezért a webhook tetszőleges számú ismétlés mellett is legfeljebb egy számlát állít ki.
A hibakódok jelentését és a bizonytalan kimenetek kezelését a [Hibakezelés](/docs/alapok/hibakezeles#bizonytalan-kimenet-számla-készült-vagy-nem) oldal írja le.
### Fizetési értesítés a Számlázz.hu-tól (IPN) [#fizetési-értesítés-a-számlázzhu-tól-ipn]
Az IPN-t a Számlázz.hu küldi, ha egy számla kifizetett összege megváltozik. Sikertelen fogadásnál 3 percenként újraküldi, legfeljebb tízszer. Válaszolj gyorsan `200`-zal, és a feldolgozás legyen idempotens:
```ts title="app/api/szamlazz/ipn/route.ts"
import { ipnOkResponse, readIpnNotification } from 'kassza/ipn'
export async function POST(request: Request) {
const ertesites = await readIpnNotification(request)
if (ertesites.isFullyPaid) await rendelesFizetve(ertesites.invoiceNumber)
return ipnOkResponse()
}
```
A `readIpnNotification()` szabványos `Request`-et vár, így Next.js route handlerben, Workersben, Denóban és Bunban is ugyanígy működik. Ahol csak a nyers törzs van meg, például AWS Lambdán, a `parseIpnNotification()` a szöveges törzset is feldolgozza. A forrás IP-cím ellenőrzését (`isSzamlazzIp()`) és a proxyval kapcsolatos buktatókat a [Hálózat és biztonság](/docs/alapok/halozat-es-biztonsag#bejövő-forgalom-a-számlázzhu-felől) oldal írja le.
### Hosszú munka sorban [#hosszú-munka-sorban]
Ha a webhookra néhány másodpercen belül válaszolni kell, a számlázást ne a webhook végezze. A webhook tegye az eseményt egy üzenetsorba (például Cloudflare Queues vagy Amazon SQS), és a feldolgozó függvény állítsa ki a számlát, mentse a PDF-et, és küldje ki az e-mailt. A feldolgozó is rendelésszám alapján keressen először, mert a sorok is kézbesíthetnek egy üzenetet többször.
## PDF mentése [#pdf-mentése]
Serverless függvényben a fájlrendszer nem tartós, ezért a PDF-et objektumtárba mentsd: S3-ba vagy R2-be az `s3FetchStorage`-dzsal, Workersben az `r2BindingStorage`-dzsal, Vercelen a `vercelBlobStorage`-dzsal. A részletek a [PDF tárhely](/docs/kiegeszitok/pdf-tarhely) oldalon vannak.
A cookie store adapterek, a saját tároló és a tároló hibáinak kezelése.
A createKassza() összes opciója, a hookok és a megszakítás.
# AI asszisztensek
URL: https://www.kasszajs.hu/docs/kiegeszitok/ai-asszisztensek
> A kassza csomagban szállított agents mappa és az llms.txt használata Claude Code, Cursor és GitHub Copilot mellett, hogy az AI jó Számlázz.hu integrációt írjon.
Az AI kódoló asszisztensek a Számlázz.hu integrációt gyakran a saját, elavult emlékeikből írják. Ilyenkor maguk számolják az áfát, ciklusban küldik újra a sikertelen számlát, vagy UTC dátumot adnak át. A kassza csomagban ezért van egy `agents/` mappa, amely az asszisztensnek szól. Pontosan a telepített verzió API-ját írja le, és felsorolja a buktatókat is.
Ez az oldal arról szól, hogyan írjon az AI jó kódot a kasszával. Ha azt szeretnéd, hogy egy
asszisztens (Claude, Cursor) közvetlenül állítson ki vagy kérdezzen le bizonylatot, az [MCP
szerver](/docs/kiegeszitok/mcp-szerver) kell.
## Mi van a csomagban? [#mi-van-a-csomagban]
Az npm csomag az `agents/` mappát és az `llms.txt` fájlt is tartalmazza, így telepítés után a projektedben is ott vannak. A fájlok angol nyelvűek.
| Fájl | Tartalom |
| --- | --- |
| `node_modules/kassza/agents/README.md` | Áttekintés, olvasási sorrend, az öt legfontosabb szabály, egy minimális példa és egy magyar–angol szótár (számla → `invoice`, díjbekérő → `proforma`, rendelésszám → `orderNumber`). |
| `node_modules/kassza/agents/pitfalls.md` | Kötelező szabályok (a nyugtákra vonatkozó jogi szabályokkal együtt), a gyakori helyzetek hibakódokkal és teendőkkel, a hibakategóriák. |
| `node_modules/kassza/agents/api.md` | Minden metódus bemenete és kimenete, az alcsomagokkal együtt. |
| `node_modules/kassza/agents/recipes.md` | 17 kész minta: megosztott kliens, pontosan egyszeri számlázás, díjbekérő és számla, IPN, pénztári nyugta, PDF mentése S3-ba vagy R2-be, serverless munkamenet, tesztek mock klienssel, vevő adatai adószámból, devizás számla EU-s vevőnek, fizetési szolgáltatók webhookja, nyugta vagy számla, NAV napi összesítő és egyeztetés, megbízotti számlázás, pénzügyi adatkapcsolat, tesztek hamis Számla Agenttel, MCP szerver. |
| `node_modules/kassza/agents/skills/kassza/SKILL.md` | Kész Claude Code skill a szabályokkal és egy ellenőrzőlistával. |
| `node_modules/kassza/llms.txt` | Tartalomjegyzék [llms.txt](https://llmstxt.org) formátumban, linkekkel a fenti fájlokra. |
Az `agents/README.md` öt szabálya, amit a generált kódban érdemes ellenőrizni:
1. A kliens egyszer jön létre, csak szerveren: `createKassza()`, a kulcs a `SZAMLAZZ_AGENT_KEY` változóból.
2. Minden számlán és nyugtán van `orderNumber` (nyugtán `callId` is), a rendelés azonosítójából képezve.
3. Az árak `netUnitPrice` vagy `grossUnitPrice` mezőben vannak a `vat` mellett, a nettó, áfa és bruttó értéket nem a kód számolja.
4. A `create` soha nem fut ciklusban. Az `invoices.createOnce()` és a `receipts.createOnce()` ezt megoldja; sima `create` után `network`, `timeout`, `partial_success` vagy `duplicate` hibánál `invoices.find({ orderNumber })` következik.
5. A tesztek a `kassza/testing` `createMockKassza()` vagy `createFakeAgentFetch()` függvényét használják.
## Claude Code [#claude-code]
A csomagban lévő skillt másold a projekted `.claude/skills/` mappájába:
```bash
mkdir -p .claude/skills
cp -r node_modules/kassza/agents/skills/kassza .claude/skills/
```
A skill leírása alapján a Claude Code magától betölti, ha a kód számlát, díjbekérőt vagy nyugtát állít ki, sztornóz, lekérdez vagy e-mailben kiküld, Számlázz.hu IPN webhookot kezel, adószámot kérdez le, vagy szóba kerül a `szamlazz.hu`, a Számla Agent vagy a `SZAMLAZZ_AGENT_KEY`. A skill előírja, hogy kódírás előtt olvassa el a `node_modules/kassza/agents/` fájljait, és a végén ellenőrzőlistán menjen végig.
A skill a `node_modules`-ból olvassa az aktuális dokumentációt, maga a `SKILL.md` viszont a
másoláskor rögzül. A kassza frissítése után futtasd újra a `cp` parancsot.
Skill nélkül is elég egy mondat a feladat elején:
```text
Használd a kassza csomagot, és előbb olvasd el a node_modules/kassza/agents/README.md-t.
```
## Cursor [#cursor]
Cursorban egy projektszabály mondja meg az asszisztensnek, hol találja a leírást:
```text title=".cursor/rules/kassza.mdc"
---
description: Számlázz.hu számlák, díjbekérők és nyugták a kassza csomaggal
alwaysApply: false
---
A Számlázz.hu integrációhoz a kassza csomagot használjuk.
Kód írása előtt olvasd el ezeket, ebben a sorrendben:
- node_modules/kassza/agents/pitfalls.md
- node_modules/kassza/agents/api.md
- node_modules/kassza/agents/recipes.md
```
A szabály törzsébe a `SKILL.md` szabályait és ellenőrzőlistáját is bemásolhatod. Ha az asszisztensed nem olvas a `node_modules` mappából, a `pitfalls.md` tartalmát másold be a szabályba.
## GitHub Copilot [#github-copilot]
Copilotnál a tároló szintű utasításokat a `.github/copilot-instructions.md` fájlba írod:
```text title=".github/copilot-instructions.md"
## Számlázás
A Számlázz.hu integrációhoz a kassza csomagot használjuk. Kód írása előtt olvasd el a
node_modules/kassza/agents/pitfalls.md, api.md és recipes.md fájlt.
Számlát soha ne küldj újra ciklusban, és a tesztekben a kassza/testing mock kliensét használd.
```
## Az llms.txt [#az-llmstxt]
A repó gyökerében lévő `llms.txt` rövid leírás a csomagról, és linkeket tartalmaz az `agents/` fájljaira, a README-re és a hivatalos Számlázz.hu dokumentációra. Az `agents/` fájlokra és a README-re mutató linkek relatívak, így mindig a fájl melletti példányra mutatnak, akár a telepített csomagban, akár a GitHubon.
Ha az asszisztens nem látja a projektedet, például egy böngészős chatben, add meg neki a fájl címét:
```text
https://raw.githubusercontent.com/futozs/kassza/main/llms.txt
```
Ennél a címnél a relatív linkek is a GitHubon lévő fájlokra mutatnak. A GitHub `main` ágán a fejlesztés alatti állapot van, a telepített csomagban pedig a nálad futó verzió dokumentációja.
Ha egy konkrét dokumentációs oldalt adnál át, használd az oldalon lévő **Oldal másolása Markdownként** gombot, és illeszd be a chatbe.
## A dokumentáció gépi változatai [#a-dokumentáció-gépi-változatai]
Ez a weboldal a teljes dokumentációt AI számára könnyen olvasható formában is kiszolgálja:
| Cím | Tartalom |
| --- | --- |
| `/llms.txt` | Az összes dokumentációs oldal listája rövid leírással. |
| `/llms-full.txt` | Az összes oldal teljes szövege egyetlen Markdown fájlban. |
| `/docs/.md` | Egy oldal Markdownként, például `/docs/alapok/hibakezeles.md`. |
Az `/llms-full.txt` akkor jó, ha az asszisztens egyszerre lássa a teljes dokumentációt. Egy konkrét kérdéshez elég a vonatkozó oldal `.md` változata.
## Ellenőrzőlista a generált kódhoz [#ellenőrzőlista-a-generált-kódhoz]
A `SKILL.md` és a `pitfalls.md` alapján ezeket nézd át, mielőtt elfogadod az AI kódját:
- A számlát kiállító webhook vagy háttérfeladat idempotens: `createOnce()`-t használ, előbb `find`-dal keres, vagy a `duplicate` hibát kezeli.
- Fizetési szolgáltató webhookjánál a `kassza/payments` kezelője és az `issueForPayment()` fut, és a nem újrapróbálható hibákat ember kapja meg.
- Nyugtát csak nem pénztárgép-köteles tevékenységhez állít ki, és csak akkor, ha mind a négy feltétel teljesül (`chooseDocument()`).
- Nincs saját újrapróbáló ciklus az `invoices.create` vagy a `receipts.create` körül.
- A dátumok nem `new Date().toISOString().slice(0, 10)` alakban készülnek, mert éjfél és hajnali 2 óra között ez a budapesti tegnapot adja.
- A PDF a `kassza/storage` modullal tárhelyre kerül, vagy az `invoices.getPdf()` hívással újra lekérhető, és nincs base64-ként az adatbázisban.
- Az IPN végpont `ipnOkResponse()`-szal, HTTP 200-zal válaszol, és a feldolgozás idempotens.
- Serverless vagy edge környezetben közös `cookieStore` van beállítva a `kassza/cookie-stores` modulból.
- Van unit teszt a `createMockKassza()` klienssel vagy integrációs teszt a `createFakeAgentFetch()`-csel, és egyik teszt sem használ valódi Agent kulcsot.
- A `createKassza()` csak szerveroldali kódban szerepel, a kulcs nem kerül a böngészőbe.
## Ha a kassza kódján dolgozol [#ha-a-kassza-kódján-dolgozol]
A repó gyökerében lévő `AGENTS.md` nem a csomag felhasználóinak szól, hanem azoknak az asszisztenseknek, amelyek magán a kassza forráskódján dolgoznak. Leírja a parancsokat, a mappaszerkezetet és a konvenciókat, például hogy a kódban nincs komment, a hibaüzenetek magyarok, nincs futásidejű függőség, és a kérés XML elemeinek sorrendje az XSD-t követi. Ez a fájl nem része az npm csomagnak.
A mock kliens, amellyel az AI által írt számlázó kód hálózat nélkül tesztelhető.
A bizonytalan kimenetű hibák és a teljes hibakód táblázat.
Számla és nyugta közvetlenül AI asszisztensből, előnézettel és megerősítéssel.
# Parancssor
URL: https://www.kasszajs.hu/docs/kiegeszitok/parancssor
> A kassza parancs (npx kassza). Környezetellenőrzés a doctor paranccsal, Agent kulcs ellenőrzése, a küldendő XML előnézete supportjegyhez, bizonylat lekérdezése és napi NAV nyugta-összesítő.
A csomag egy `kassza` parancsot is ad. Telepítés nélkül az `npx kassza` futtatja, egy projektben a `node_modules/.bin/kassza`. Node.js 22 vagy újabb kell hozzá. Az Agent kulcsot a `SZAMLAZZ_AGENT_KEY` környezeti változóból veszi.
```bash
npx kassza help
```
## doctor [#doctor]
```bash
npx kassza doctor
```
```text
✓ Node.js 22.11.0
✓ Időzóna-adatok: Europe/Budapest elérhető.
✓ Agent kulcs: 40 karakter, kisbetűs.
✓ Hitelesítés: a Számlázz.hu elfogadta az Agent kulcsot.
! Óra: a gép órája 9,5 mp-et siet; éjfél körül 9,5 mp ideig rossz napra kerülhet a bizonylat kelte (352-es hiba). Szinkronizáld az órát (NTP).
✓ Munkamenet: a Számlázz.hu munkamenet-sütit adott, a kassza újrahasznosítja.
- Fiók típusa: add meg egy meglévő bizonylat számát (--invoice vagy --receipt), abból kiderül, hogy teszt- vagy éles fiók.
Összegzés: 0 hiba, 1 figyelmeztetés.
```
| Ellenőrzés | Mit néz |
| --- | --- |
| Node.js | Legalább 22-es verzió. |
| Időzóna-adatok | Elérhető-e az `Europe/Budapest` időzóna (a kassza ebben számolja a dátumokat). |
| Agent kulcs | Megvan-e, kisbetűs-e, nincs-e körülötte szóköz. A kulcsot nem írja ki. |
| Hitelesítés | Egy nem létező számla lekérésével próbálja ki a kulcsot; bizonylatot nem hoz létre. |
| Óra | A gép órájának eltérése a Számlázz.hu válaszának `Date` fejlécétől. Éjfél körül néhány másodperc eltérés is rossz kelte dátumot okozhat. |
| Munkamenet | Kapott-e munkamenet-sütit. Ha egy proxy kiszűri, minden kérés új bejelentkezés lesz. |
| Fiók típusa | A `--invoice ` vagy `--receipt ` megadásával a bizonylatból megállapítja, teszt- vagy éles fiók-e. |
Hiba esetén 1-es kilépési kóddal áll le, így CI-ban vagy telepítési szkriptben is használható. A `--json` kapcsoló gépi feldolgozásra alkalmas kimenetet ad.
### Bővített ellenőrzés [#bővített-ellenőrzés]
| Kapcsoló | Mit csinál |
| --- | --- |
| `--capabilities` | Előnézettel megnézi, engedélyezett-e az e-számla (bizonylat nem készül). A nyugta-előtagot csak valódi kiállítás ellenőrizheti, ezt jelzi. |
| `--check-update` | Lekérdezi az npm-ről a legfrissebb kassza verziót (hálózati hívás, ezért csak kérésre). |
| `--report` | Titokmentes Markdown jelentést ír hibajegyhez vagy a Számlázz.hu ügyfélszolgálatnak: verziók és az ellenőrzések eredménye, Agent kulcs és bizonylatadat nélkül. |
Ha egy bizonylatból kiderül, hogy éles fiókhoz tartozik a kulcs, és közben `NODE_ENV=test` vagy `development`, a doctor figyelmeztet: a tesztjeid valódi bizonylatot állíthatnak ki.
## verify [#verify]
```bash
npx kassza verify
```
Csak az Agent kulcsot ellenőrzi. Érvényes kulcsnál 0, elutasított kulcsnál 1 a kilépési kód.
## xml preview [#xml-preview]
A Számlázz.hu ügyfélszolgálata egy hibánál leggyakrabban a pontos XML-t kéri. Az `xml preview` kiírja azt az XML-t, amit a kassza az adott bemenetre küldene. Az Agent kulcs helyén `***` áll, így a kimenet nyugodtan elküldhető.
```bash
npx kassza xml preview szamla.json
npx kassza xml preview nyugta.json --type receipt --defaults alapok.json
cat szamla.json | npx kassza xml preview -
```
A bemenet a `invoices.create()` (vagy `--type receipt` esetén a `receipts.create()`) bemenete JSON-ban. Az alapértelmezéseket a `--defaults` fájlban (a `createKassza()` `defaults` opciója), vagy egy `{ "defaults": …, "input": … }` szerkezetű bemenetben is megadhatod.
## invoice get, receipt get [#invoice-get-receipt-get]
```bash
npx kassza invoice get WEB-2026-118
npx kassza invoice get --order RENDELES-42
npx kassza invoice get --external CRM-42
npx kassza receipt get NYGT-2026-31
npx kassza receipt get --order RENDELES-42
```
A bizonylatot JSON-ban írja ki, PDF nélkül. Ha nincs ilyen bizonylat, 1-es kóddal áll le.
## nav summary [#nav-summary]
```bash
npx kassza nav summary --file nyugtak.json --from 2026-09-01 --to 2026-09-30
```
Egy JSON fájlban lévő nyugtalistából (kassza `Receipt` objektumok tömbje, vagy `{ "receipts": [...] }`) elkészíti a [napi NAV-összesítőt](/docs/nav-nyugta/napi-osszesito). A `--from` és `--to` a kelte szerint szűr, az `--include-test` a tesztfiókos nyugtákat is beveszi, a `--json` gépi kimenetet ad.
## mcp [#mcp]
```bash
npx kassza mcp
```
MCP szervert indít a szabványos bemeneten és kimeneten, AI asszisztensekhez. Lásd [MCP szerver](/docs/kiegeszitok/mcp-szerver).
## Kilépési kódok [#kilépési-kódok]
| Kód | Jelentés |
| --- | --- |
| `0` | Sikeres |
| `1` | Hiba (hitelesítés, Számlázz.hu hiba, nem található bizonylat, hibás fájl) |
| `2` | Hibás használat (ismeretlen parancs vagy kapcsoló) |
# MCP szerver
URL: https://www.kasszajs.hu/docs/kiegeszitok/mcp-szerver
> A kassza MCP szervere Claude, Cursor és más MCP kliensek számára. Számla, nyugta és sztornó előnézettel és megerősítéssel, lekérdezések, adószám-ellenőrzés és napi NAV-összesítő. Alapból csak olvas.
A kassza a Model Context Protocol (MCP) szerint is elérhető, így egy AI asszisztens közvetlenül kérdezhet le vagy állíthat ki bizonylatot: „Állíts ki számlát a Példa Kft.-nek 3 óra tanácsadásról.” A szerver a `kassza mcp` paranccsal indul, a szabványos bemeneten és kimeneten (stdio) kommunikál, és nem kell hozzá semmilyen függőség.
Egy AI asszisztens valódi számlát is kiállíthat, ha engeded. Először Számlázz.hu tesztfiókkal
próbáld ki, és az írási jogot csak akkor kapcsold be, ha a megerősítési folyamat neked is
megfelel.
## Beállítás [#beállítás]
### Claude Desktop [#claude-desktop]
A `claude_desktop_config.json` fájlban:
```json title="claude_desktop_config.json"
{
"mcpServers": {
"kassza": {
"command": "npx",
"args": ["-y", "kassza", "mcp"],
"env": { "SZAMLAZZ_AGENT_KEY": "..." }
}
}
}
```
### Claude Code [#claude-code]
```bash
claude mcp add kassza --env SZAMLAZZ_AGENT_KEY=... -- npx -y kassza mcp
```
### Cursor [#cursor]
```json title=".cursor/mcp.json"
{
"mcpServers": {
"kassza": {
"command": "npx",
"args": ["-y", "kassza", "mcp"],
"env": { "SZAMLAZZ_AGENT_KEY": "..." }
}
}
}
```
Az alapértelmezéseket (például a nyugta előtagját és fizetési módját) a `--defaults alapok.json` kapcsolóval adhatod meg; a fájl a `createKassza()` `defaults` opciója JSON-ban.
## Eszközök [#eszközök]
| Eszköz | Mit csinál | Ír? |
| --- | --- | --- |
| `preview_invoice` | Számla vagy díjbekérő előnézete: tételek, áfa, végösszeg, dátumok. A Számlázz.hu-t nem hívja. | Nem |
| `preview_receipt` | Nyugta előnézete a forintos kerekítési szabályok ellenőrzésével. | Nem |
| `preview_reversal` | Lekéri a sztornózandó számlát vagy nyugtát, és ellenőrzi, sztornózható-e. | Nem |
| `create_invoice` | Számla vagy díjbekérő kiállítása. | Igen |
| `create_receipt` | Nyugta kiállítása. | Igen |
| `reverse_invoice`, `reverse_receipt` | Sztornó. | Igen |
| `get_invoice` | Számla lekérése számlaszám, rendelésszám vagy külső azonosító alapján. | Nem |
| `get_receipt` | Nyugta lekérése nyugtaszám vagy rendelésszám alapján. | Nem |
| `query_taxpayer` | Adószám ellenőrzése a NAV adatai alapján, cégnévvel és székhellyel. | Nem |
| `nav_daily_summary` | A megadott nyugtákból (vagy egy sorszámtartományból) a napi NAV-összesítő. | Nem |
## Biztonság [#biztonság]
**Alapból csak olvas.** Az író eszközök (`create_*`, `reverse_*`) csak akkor jelennek meg, ha a szervert írási joggal indítod:
```json
"args": ["-y", "kassza", "mcp", "--allow-write"]
```
vagy a `KASSZA_MCP_ALLOW_WRITE=1` környezeti változóval.
**Megerősítés nélkül nem ír.** Minden író eszköz egy megerősítő kódot vár, amelyet csak a hozzá tartozó előnézeti eszköz ad. A folyamat:
1. Az asszisztens meghívja a `preview_invoice` eszközt, és megmutatja neked az előnézetet (vevő, tételek, végösszeg, dátumok, hogy kap-e a vevő e-mailt).
2. Ha jóváhagyod, a `create_invoice` eszközt pontosan ugyanazokkal az adatokkal és a kapott kóddal hívja.
A kód 15 percig érvényes, és csak ugyanarra a kérésre jó: ha az asszisztens bármit megváltoztat az adatokon, a kiállítás elutasítva, új előnézet kell. A kódot a szerver egy indításonként véletlen titokkal írja alá, így kitalálni nem lehet.
**Pontosan egyszer.** A `create_invoice` a kiállítás előtt külső azonosító alapján megkeresi a már elkészült számlát, így egy újrapróbált hívás nem állít ki másodikat. A `create_receipt` hívásazonosítóval dolgozik, rendelésszámmal pedig egy rendeléshez egy nyugtát enged. A sztornó eszközök egy bizonylatot csak egyszer sztornóznak.
**Nem küld e-mailt magától.** A `create_invoice` alapból nem küldi el a számlát a vevőnek; ehhez a `sendEmail: true` mező kell, ami az előnézetben is látszik.
## Protokoll [#protokoll]
A szerver az MCP 2026-07-28-as, állapotmentes változatát beszéli (`server/discover`, kérésenkénti `_meta`), és a régebbi, `initialize` kézfogásos klienseket is kiszolgálja (2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05). Soronként egy JSON-RPC 2.0 üzenet megy; a naplózás a szabványos hibakimenetre kerül, a kimenetet nem szennyezi.
A futtatókörnyezet-független szerver a `kassza/mcp` modulból saját átvitellel is használható:
```ts
import { createKasszaMcpServer } from 'kassza/mcp'
const server = createKasszaMcpServer({ agentKey: process.env.SZAMLAZZ_AGENT_KEY! })
const kapcsolat = server.connect()
const valasz = await kapcsolat.handle(uzenet)
```
## Kapcsolódó [#kapcsolódó]
Az agents mappa és az llms.txt, ha az asszisztens kódot ír.
A kassza parancs többi része.
# Tárolók
URL: https://www.kasszajs.hu/docs/kiegeszitok/tarolok
> A kassza/stores alkönyvtár közös kulcs-érték tárolói (Redis, Upstash, Cloudflare KV, Durable Object), az atomikus műveletek, és hogy melyik tároló mire alkalmas.
Több kassza funkció közös tárolót használ: a munkamenet (`cookieStore`), a próbálkozás-napló (`attemptLedger`), a `createOnce` zárja (`createOnceLock`), a webhookok ismétlés-szűrője (`dedupe`), az adószám-gyorsítótár és a [bizonylatnapló](/docs/kiegeszitok/bizonylatnaplo). Mindegyik ugyanazt a `KeyValueStore` felületet várja, amelyet a `kassza/stores` adapterei adnak.
```ts
import { Redis } from '@upstash/redis'
import { createKassza } from 'kassza'
import { upstashRedisStore } from 'kassza/stores'
const store = upstashRedisStore(Redis.fromEnv())
export const kassza = createKassza({
cookieStore: store,
attemptLedger: store,
createOnceLock: store,
})
```
| Adapter | Kliens | Atomikus műveletek |
| --- | --- | --- |
| `memoryStore()` | – (egy folyamaton belül) | igen |
| `upstashRedisStore(redis)` | `@upstash/redis` | igen (EVAL) |
| `ioredisStore(redis)` | `ioredis` | igen (EVAL) |
| `nodeRedisStore(client)` | `redis` (node-redis) | igen (EVAL) |
| `durableObjectStore(stub)` | Cloudflare Durable Object | igen |
| `cloudflareKvStore(namespace)` | Cloudflare KV | **nem** |
| `customStore(store)` | saját `get` / `set` / `delete` | ha a sajátod tud |
A régi nevek (`upstashRedisCookieStore` és társai) és a `kassza/cookie-stores` útvonal változatlanul működnek.
## Atomikus műveletek [#atomikus-műveletek]
A `get`, `set` és `delete` mellett a felület három nem kötelező, atomikus műveletet ismer:
| Művelet | Mire kell |
| --- | --- |
| `setIfAbsent(key, value, ttl)` | A `createOnce` elosztott zárja. Nélküle a zár nem működik. |
| `increment(key, ttl)` | A próbálkozás-napló pontos számlálása, és a bizonylatnapló. |
| `deleteIfEquals(key, value)` | A zár biztonságos felszabadítása (csak a saját zárát törli). |
A Redis alapú adapterek ezeket Lua szkripttel valósítják meg, ha a kliens tud `eval`-t. A Cloudflare KV erre nem alkalmas, mert nincs benne atomikus írás: zárhoz és naplóhoz Workers alatt a Durable Object tárolót használd.
## Durable Object tároló (Cloudflare Workers) [#durable-object-tároló-cloudflare-workers]
```ts title="src/kassza-store.ts"
import { DurableObject } from 'cloudflare:workers'
import { handleDurableObjectStoreRequest } from 'kassza/stores'
export class KasszaStore extends DurableObject {
fetch(request: Request): Promise {
return handleDurableObjectStoreRequest(this.ctx.storage, request)
}
}
```
```ts title="src/index.ts"
import { createKassza } from 'kassza'
import { durableObjectStore } from 'kassza/stores'
export { KasszaStore } from './kassza-store'
export default {
async fetch(request: Request, env: Env): Promise {
const store = durableObjectStore(env.KASSZA_STORE.get(env.KASSZA_STORE.idFromName('kassza')))
const kassza = createKassza({ agentKey: env.SZAMLAZZ_AGENT_KEY, cookieStore: store, createOnceLock: store })
return new Response('ok')
},
}
```
A Durable Object egyszerre egy kérést dolgoz fel, ezért a műveletei atomikusak. A lejárt kulcsokat olvasáskor törli.
## Hibatűrés [#hibatűrés]
Az adapterek alapból hibatűrők: ha a `get`, `set` vagy `delete` nem sikerül, a kérés munkamenet nélkül folytatódik, és az `onError` (vagy a konzol) kap jelzést. Az atomikus műveletek hibáját viszont továbbadják, mert ezekről a kassza dönt (például a zárnál `store_unavailable` hibát dob). Ha minden hibát a kasszára bíznál, hozd létre az adaptert `resilient: false` beállítással.
## Melyik tároló mire jó? [#melyik-tároló-mire-jó]
```ts
import { diagnoseStore } from 'kassza/stores'
const eredmeny = await diagnoseStore(store)
console.log(eredmeny.suitableFor, eredmeny.problems)
```
A `diagnoseStore()` egy ideiglenes kulccsal kipróbálja az írást, olvasást, törlést és az atomikus műveleteket, majd megmondja, hogy a tároló alkalmas-e munkamenetre, próbálkozás-naplóra, zárra, bizonylatnaplóra és ismétlés-szűrőre. A kulcsot a végén törli.
# Bizonylatnapló
URL: https://www.kasszajs.hu/docs/kiegeszitok/bizonylatnaplo
> A kassza/journal megbízható bizonylatnaplója a NAV napi nyugta-összesítőhöz és a napi záráshoz, befejezetlen tételek kezelésével és az adatkapcsolat archívumával való egyeztetéssel.
A Számla Agent nem tud nyugtákat dátum szerint listázni, ezért a [NAV napi összesítő](/docs/nav-nyugta/napi-osszesito) és a napi zárás a **saját nyilvántartásodból** számol. A `kassza/journal` ezt a nyilvántartást adja: minden elkészült bizonylatot rögzít, és jelzi, ha egy kiállítás kimenetele bizonytalan maradt.
```ts title="lib/kassza.ts"
import { Redis } from '@upstash/redis'
import { createKassza } from 'kassza'
import { createJournal, kvJournal } from 'kassza/journal'
import { upstashRedisStore } from 'kassza/stores'
const store = upstashRedisStore(Redis.fromEnv())
export const journal = createJournal(kvJournal(store))
export const kassza = createKassza({
createOnceLock: store,
hooks: {
onDocument: (event) => journal.record(event),
onDocumentError: 'throw',
},
})
```
Az `onDocumentError: 'throw'` miatt ha a naplóba írás nem sikerül, a hívás `DocumentHookError`-t dob (benne a már elkészült bizonylattal), így a kimaradt bejegyzés nem marad észrevétlen.
## NAV napi összesítő a naplóból [#nav-napi-összesítő-a-naplóból]
```ts
import { navDailyReports } from 'kassza/reports'
const nyugtak = await journal.receipts({ from: '2026-10-01', to: '2026-10-31' })
const jelentesek = navDailyReports(nyugtak)
```
A `receipts()` a teszt nyugtákat alapból kihagyja (`includeTest: true` esetén benne vannak). Egy lekérdezés legfeljebb 366 napot fog át.
## Befejezetlen tételek: írj előbb, aztán állíts ki [#befejezetlen-tételek-írj-előbb-aztán-állíts-ki]
Ha a folyamat a kiállítás közben leáll (serverless időkorlát, összeomlás), a napló nem tudja, hogy elkészült-e a bizonylat. A `trackReceipt()` és a `trackInvoice()` ezt kezeli: a kiállítás előtt befejezetlen tételt rögzít, utána lezárja.
```ts
const eredmeny = await journal.trackReceipt(rendeles.id, () =>
kassza.receipts.createOnce({ orderNumber: rendeles.id, items: rendeles.tetelek }),
)
```
| Kimenet | A napló állapota |
| --- | --- |
| Sikeres kiállítás (vagy már létező bizonylat) | Rögzítve, a befejezetlen tétel törölve. |
| Biztos hiba (például validáció) | Nincs bizonylat, a befejezetlen tétel törölve. |
| Bizonytalan hiba (időtúllépés, hálózat) vagy összeomlás | A tétel befejezetlen marad. |
| A bizonylat elkészült, de a naplóba írás elbukott | `JournalWriteError`, benne az eredménnyel; a tétel befejezetlen marad. |
A befejezetlen tételeket a `settle()` zárja le: rendelésszám alapján megkeresi a bizonylatokat a Számlázz.hu-n, és amit megtalál, rögzíti.
```ts
const nyitott = await journal.pending()
const rendezes = await journal.settle(kassza)
console.log(rendezes.recorded.length, rendezes.notFound.length, rendezes.failed.length)
```
A meg nem talált tételek alapból befejezetlenek maradnak (`releaseNotFound: true` esetén törlődnek), mert egy meg nem talált bizonylat azt jelenti, hogy a rendeléshez még ki kell állítani.
## Egyeztetés az adatkapcsolat archívumával [#egyeztetés-az-adatkapcsolat-archívumával]
A Számlázz.hu [adatkapcsolata](/docs/adatkapcsolat) a napi nyugtaarchívumot is elküldheti. Ez a forrás-igazság: a `reconcile()` pótolja a naplóból hiányzó nyugtákat, és jelzi azokat, amelyek a naplóban vannak, de az archívumban nincsenek.
```ts
const egyeztetes = await journal.reconcile(push.receipts)
console.log(egyeztetes.added, egyeztetes.missingFromArchive)
```
`dryRun: true` esetén csak jelent, nem ír.
## Tárolók [#tárolók]
| Tároló | Mikor |
| --- | --- |
| `kvJournal(store)` | Redis, Upstash vagy Durable Object; a tárolónak tudnia kell atomikus számlálót (`increment`). |
| `memoryJournal()` | Tesztekhez és egyetlen folyamathoz. |
| Saját `JournalStorage` | SQL adatbázishoz: hat metódust kell megírnod (bejegyzés írása, olvasása, listázása, és ugyanez a befejezetlen tételekre). |
A `kvJournal` alapból 400 napig őrzi a bejegyzéseket (`retentionDays`), a befejezetlen tételeket 30 napig. A napló nem helyettesíti a Számlázz.hu archívumát, csak a napi jelentésekhez és egyeztetéshez kell.
# Node.js, Express, NestJS
URL: https://www.kasszajs.hu/docs/kiegeszitok/node-express
> A kassza webhook-kezelőinek (fizetések, IPN, adatkapcsolat) bekötése Express, NestJS és Fastify alá a kassza/node adapterrel, a nyers törzs megőrzésével.
A kassza webhook-kezelői Web-szabványos `(Request) => Promise` függvények. Next.js, Hono, Cloudflare Workers, Bun és Deno alatt ezek közvetlenül használhatók. Express, NestJS és Fastify alatt a `kassza/node` adapter köti be őket.
```ts title="server.ts"
import express from 'express'
import { toNodeHandler } from 'kassza/node'
import { stripeWebhook } from 'kassza/payments/stripe'
const app = express()
app.post(
'/webhooks/stripe',
toNodeHandler(
stripeWebhook({
secret: process.env.STRIPE_WEBHOOK_SECRET ?? '',
onPayment: (payment) => kassza.issueForPayment(payment, { vat: 27 }),
}),
),
)
app.use(express.json())
```
A fizetési szolgáltatók a törzs bájtjait írják alá. Ha az `express.json()` előbb feldolgozza a
törzset, az aláírás nem ellenőrizhető. Tedd a webhook útvonalat az `express.json()` elé (mint
fent), vagy használd rajta az `express.raw({ type: '*/*' })` middleware-t. Ha a törzset egy
middleware már feldolgozta, a `toNodeHandler` érthető hibával (`NodeBodyError`) jelez.
A `toNodeHandler` a kérést folyamként adja tovább, így a kezelők saját méretkorlátja (például 1 MB a fizetési webhookoknál) a beolvasás közben érvényesül. A query string is átmegy, ami a Barion callbackhez kell.
| Opció | Jelentés |
| --- | --- |
| `trustProxy` | `true` esetén az `x-forwarded-proto` és `x-forwarded-host` fejlécből állítja össze az URL-t. Csak akkor kapcsold be, ha a proxyd írja ezeket. |
| `onError` | A váratlan hibák naplózása (alapból a konzol). Express `next` esetén a hiba oda megy. |
## NestJS [#nestjs]
```ts title="main.ts"
const app = await NestFactory.create(AppModule, { rawBody: true })
```
```ts title="webhook.controller.ts"
import { Controller, Post, Req, Res } from '@nestjs/common'
import type { Request, Response } from 'express'
import { toNodeHandler } from 'kassza/node'
import { stripeWebhook } from 'kassza/payments/stripe'
const handler = toNodeHandler(stripeWebhook({ secret: process.env.STRIPE_WEBHOOK_SECRET ?? '', onPayment }))
@Controller('webhooks')
export class WebhookController {
@Post('stripe')
stripe(@Req() request: Request, @Res() response: Response): Promise {
return handler(request, response)
}
}
```
A `rawBody: true` miatt a NestJS a `request.rawBody`-ban megőrzi a nyers törzset, és a `toNodeHandler` azt használja.
## Fastify [#fastify]
```ts
import { toWebRequest } from 'kassza/node'
import { stripeWebhook } from 'kassza/payments/stripe'
const webhook = stripeWebhook({ secret: process.env.STRIPE_WEBHOOK_SECRET ?? '', onPayment })
fastify.addContentTypeParser('*', { parseAs: 'buffer' }, (_request, body, done) => done(null, body))
fastify.post('/webhooks/stripe', async (request, reply) => {
const response = await webhook(
toWebRequest({ method: request.method, url: request.url, headers: request.headers, body: request.body }),
)
reply.status(response.status).send(Buffer.from(await response.arrayBuffer()))
})
```
Fastify alatt a `toWebRequest()` és a Web-szabványos kezelő közvetlen hívása a legegyszerűbb: a nyers `Buffer` törzset adod át, a választ pedig a `reply`-ra írod.
# Tömeges és ismétlődő számlázás
URL: https://www.kasszajs.hu/docs/kiegeszitok/tomeges-szamlazas
> Havi díjak, tagdíjak, bérleti díjak biztonságos kiállítása a kassza/batch modullal - idempotens kulcsok, sebességkorlát, próbafuttatás, leállás a Számlázz.hu korlátjánál, és hónap végét helyesen kezelő időszakok.
Havonta sok számla kiállítása pont az a helyzet, ahol a Számlázz.hu szabályai a leginkább számítanak: ne küldj túl gyorsan, és egy hibás kérést ne ismételj. A `kassza/batch` a végrehajtást adja; az ütemezést (cron, várólista) a platformod.
```ts
import { billingPeriodAt, runBatch } from 'kassza/batch'
const idoszak = billingPeriodAt({ interval: 'month', anchor: '2026-01-31', paymentDueInDays: 8 }, new Date())
const eredmeny = await runBatch(kassza, {
items: elofizetesek.map((elofizetes) => ({
key: `SUB-${elofizetes.id}-${idoszak.start}`,
document: () => ({
kind: 'invoice',
input: {
buyer: elofizetes.vevo,
dueDate: idoszak.dueDate,
items: [{ name: `Havidíj (${idoszak.start} – ${idoszak.end})`, grossUnitPrice: elofizetes.dij, vat: 27 }],
},
}),
})),
ratePerMinute: 30,
journal,
onProgress: (allapot) => console.log(`${allapot.done}/${allapot.total}`),
})
console.log(eredmeny.created.length, eredmeny.existing.length, eredmeny.failed)
```
## Amit garantál [#amit-garantál]
| Tulajdonság | Hogyan |
| --- | --- |
| Újraindítható | Minden tétel a `key`-t kapja rendelésszámnak, és `createOnce`-szal készül. Egy megszakadt futás újraindítva a már kiállítottakat `existing`-ként jelzi, nem állítja ki újra. |
| Kíméletes | Alapból percenként legfeljebb 30 tétel indul, egyszerre egy (`concurrency` legfeljebb 4). |
| Leáll, ha kell | Tesztfiók-korlát (167), hitelesítési, fiók-, karbantartási, kitiltási vagy tároló-hibánál nem indít új tételt; a többit `skipped`-ként adja vissza. Automatikusan soha nem próbálja újra. |
| Hibaizoláció | Egy tétel adathibája (például validáció) nem állítja meg a többit; a végén a `failed` lista tartalmazza. |
| Megszakítható | `signal` (AbortSignal) esetén nem indít új tételt. |
## Próbafuttatás [#próbafuttatás]
`dryRun: true` esetén minden számlához előnézetet kér (a Számlázz.hu kiszámolja, de nem állítja ki), a nyugtákat helyben számolja ki. Semmi nem készül el, a végén a `previewed` lista és a `grossTotal` mutatja, mit állítanál ki.
## Időszakok [#időszakok]
| Függvény | Mit ad |
| --- | --- |
| `billingPeriod(beallitas, n)` | Az n-edik időszak (0-tól): `start`, `end`, `dueDate`, `key`. |
| `billingPeriodAt(beallitas, datum)` | A dátumot tartalmazó időszak. |
| `billingPeriodsBetween(beallitas, tol, ig)` | Az időszakok egy tartományban. |
| `todayPeriod(beallitas)` | A mai (budapesti dátum szerinti) időszak. |
A hónap végi kezdőnapot csúszás nélkül kezeli: a január 31-én induló havi előfizetés időszakai február 28-án (szökőévben 29-én), majd március 31-én kezdődnek, mert mindig az első naptól számol. Az időszakok hézag és átfedés nélkül követik egymást.
Időszakos elszámolásnál a teljesítés dátumára az áfatörvény külön szabályt ad. A kassza nem dönt
helyetted: a `start`, `end` és `dueDate` alapján add meg a `fulfillmentDate`-et a könyvelőddel
egyeztetett szabály szerint.
# Megfigyelhetőség
URL: https://www.kasszajs.hu/docs/kiegeszitok/megfigyelhetoseg
> Strukturált napló, OpenTelemetry-kompatibilis tracing és Prometheus metrikák a kassza/observe modullal - függőség nélkül, titokmentesen, és semmit nem küld haza.
A kassza nem küld semmilyen használati adatot. A saját rendszeredben viszont látnod kell, mi történik: hány kérés ment ki, mennyi ideig tartott, mi bukott el, és mikor esett ki egy tároló. Erre való a `kassza/observe`.
```ts
import { createKassza } from 'kassza'
import { combineHooks, createMetricsRegistry, observe } from 'kassza/observe'
import { trace } from '@opentelemetry/api'
export const metrics = createMetricsRegistry()
export const kassza = createKassza({
hooks: combineHooks(
observe({ logger, tracer: trace.getTracer('kassza'), metrics }),
{ onDocument: (event) => journal.record(event), onDocumentError: 'throw' },
),
})
```
## Napló [#napló]
Minden próbálkozásról egy strukturált sor készül (`event: 'kassza.request'`, művelet, próbálkozás, időtartam, kimenet, hibakategória és -kód), a figyelmeztetésekről (`kassza.warning`) és az elkészült bizonylatokról (`kassza.document`). A `logger` lehet `console`, pino, vagy bármi, aminek `info(adat, üzenet)` és `warn(adat, üzenet)` metódusa van.
A napló titokmentes: az Agent kulcs, a vevő neve, címe, adószáma és e-mailje sosem kerül bele. A rendelésszám helyett egy kulcsolt hash (`orderRef: 'hmac:…'`) jelenik meg, amely a naplóban korrelálható, de nem visszafejthető. Ha több folyamat naplóját is össze akarod kötni, add meg ugyanazt az `orderRefSalt` titkot; ha a nyílt rendelésszám kell, `redact: false`.
## Tracing [#tracing]
A `tracer` bármi lehet, aminek `startSpan(név, { attributes })` metódusa van, így az OpenTelemetry `Tracer` közvetlenül megfelel (a kassza nem importálja az OpenTelemetry-t). Minden próbálkozás egy span: `kassza.action`, `kassza.attempt`, `kassza.request_id`, `http.status_code`, `kassza.error.category`, `kassza.error.code`.
## Metrikák [#metrikák]
A `createMetricsRegistry()` függőség nélküli regiszter, amely Prometheus szövegformátumban is kiírja magát:
```ts
export function GET(): Response {
return new Response(metrics.renderPrometheus(), { headers: { 'content-type': 'text/plain; version=0.0.4' } })
}
```
| Metrika | Jelentés |
| --- | --- |
| `kassza_requests_total{action,outcome}` | Próbálkozások kimenet (siker vagy hibakategória) szerint. |
| `kassza_request_duration_ms{action}` | Időtartam hisztogram. |
| `kassza_retries_total{action}` | Automatikus újrapróbálások. |
| `kassza_maintenance_total` | Karbantartás miatti hibák. |
| `kassza_unexpected_response_total{action}` | Ismeretlen formátumú Számlázz.hu válaszok. |
| `kassza_store_errors_total{store}` | Tároló-hibák (munkamenet, napló, zár, ismétlés-szűrő, gyorsítótár). |
| `kassza_warnings_total{kind}` | Figyelmeztetések. |
| `kassza_documents_total{kind,action}` | Elkészült bizonylatok. |
Saját metrikarendszerhez (StatsD, OpenTelemetry Metrics) elég egy `{ counter, histogram }` objektumot átadni.
## Riasztási minták [#riasztási-minták]
| Feltétel | Mit jelent |
| --- | --- |
| `kassza_store_errors_total` nő | Egy tároló kiesett; a kitiltás elleni védelem vagy a zár részben nem él. |
| `kassza_maintenance_total` percekig nő | A Számlázz.hu karbantart; válts tartalék folyamatra. |
| `kassza_unexpected_response_total > 0` | A Számlázz.hu megváltoztatott valamit; nézd meg a hibát és jelezd a kassza hibajegyében. |
| `kassza_requests_total{outcome="auth"}` nő | Az Agent kulcs érvénytelen lett. |
A `combineHooks()` több hook-készletet fűz össze: mindegyik lefut, és ha valamelyik hibát dob, a hiba a végén jelentkezik.
# Hibakódok
URL: https://www.kasszajs.hu/docs/hibakodok
> A Számlázz.hu Számla Agent hibakódjai egyenként, magyarázattal és kassza-példával.
Minden hibakódhoz külön oldal tartozik: mit jelent, mikor jön elő, mi a teendő, és hogyan kezeld a kódban. A kassza hibáin a `docsUrl` mező közvetlenül ide mutat, így a naplóból egy kattintással megnyitható.
```ts
console.error(error.message, error.hint, error.docsUrl)
```
| Kód | Üzenet | Kategória |
| --- | --- | --- |
| [1](/docs/hibakodok/1) | Rendszerkarbantartás, kérem próbálja meg pár perc múlva. | `maintenance` |
| [3](/docs/hibakodok/3) | Sikertelen bejelentkezés. | `auth` |
| [7](/docs/hibakodok/7) | Hiányzó adat: ismeretlen számlaszám, rendelésszám vagy külső azonosító. | `not_found` |
| [8](/docs/hibakodok/8) | Nincs jogosultság vagy megfelelő díjcsomag a művelet elvégzéséhez. | `account` |
| [17](/docs/hibakodok/17) | Nem megengedett művelet. | `account` |
| [49](/docs/hibakodok/49) | A tanúsítvány használatához jelszó szükséges. | `account` |
| [52](/docs/hibakodok/52) | Az áfakulcs nem értelmezhető. | `validation` |
| [53](/docs/hibakodok/53) | Hiányzó XML fájl. | `validation` |
| [54](/docs/hibakodok/54) | E-számla készítés nincs engedélyezve. | `account` |
| [55](/docs/hibakodok/55) | E-számla aláírása sikertelen. | `account` |
| [56](/docs/hibakodok/56) | A bizonylat elkészült, de az értesítő e-mail kiküldése sikertelen. | `partial_success` |
| [57](/docs/hibakodok/57) | XML beolvasási hiba. | `validation` |
| [68](/docs/hibakodok/68) | Már létezik cég ezzel az adószámmal és számlaszám-előtaggal. | `validation` |
| [71](/docs/hibakodok/71) | Már létező rendelésszám. | `duplicate` |
| [101](/docs/hibakodok/101) | A bejelentkezési név már foglalt. | `validation` |
| [135](/docs/hibakodok/135) | Számla Agent futtatásához lépj ki a Számlázz.hu rendszerből a böngészőben. | `auth` |
| [136](/docs/hibakodok/136) | Bejelentkezési hiba, lépj be a Számlázz.hu rendszerébe böngészőn keresztül. | `account` |
| [137](/docs/hibakodok/137) | Nincs engedély számlázási fiók létrehozására Számla Agenten keresztül. | `account` |
| [152](/docs/hibakodok/152) | Már létező rendelésszám. | `duplicate` |
| [164](/docs/hibakodok/164) | A funkciót csak egyetlen fiókhoz hozzáférő felhasználó használhatja. | `auth` |
| [167](/docs/hibakodok/167) | A tesztfiókban túllépted a rövid idő alatt kiállítható bizonylatok számát. | `rate_limit` |
| [200](/docs/hibakodok/200) | Ezt az előtagot ugyanezzel az adószámmal egy másik számlázási fiók használja. | `validation` |
| [202](/docs/hibakodok/202) | A megadott számlaszám előtag nem megfelelő. | `validation` |
| [250](/docs/hibakodok/250) | A fiók nem használható, mert a fiókgazda még nem vette birtokba, vagy a meghatalmazás nincs elfogadva. | `account` |
| [259](/docs/hibakodok/259) | A tétel nettó értéke nem megfelelő. | `validation` |
| [260](/docs/hibakodok/260) | A tétel áfa értéke nem megfelelő. | `validation` |
| [261](/docs/hibakodok/261) | A tétel bruttó értéke nem megfelelő. | `validation` |
| [262](/docs/hibakodok/262) | A tétel nettó értéke nem megfelelő. | `validation` |
| [263](/docs/hibakodok/263) | A tétel áfa értéke nem megfelelő. | `validation` |
| [264](/docs/hibakodok/264) | A tétel bruttó értéke nem megfelelő. | `validation` |
| [309](/docs/hibakodok/309) | Érvénytelen adat. | `validation` |
| [335](/docs/hibakodok/335) | A hivatkozott díjbekérő nem található. | `not_found` |
| [336](/docs/hibakodok/336) | A nyugta előtag már számlákhoz használatban van. | `validation` |
| [337](/docs/hibakodok/337) | A nyugta előtag formátuma hibás. | `validation` |
| [338](/docs/hibakodok/338) | A hívásazonosító már létezik. | `duplicate` |
| [339](/docs/hibakodok/339) | A nyugtaszám nem létezik. | `not_found` |
| [340](/docs/hibakodok/340) | A kifizetett összeg eltér a bruttó végösszegtől. | `validation` |
| [352](/docs/hibakodok/352) | A számla kelte csak a mai nap lehet. | `validation` |
| [353](/docs/hibakodok/353) | Nincs egyetlen érvényes számlaszám-előtag sem. | `account` |
| [354](/docs/hibakodok/354) | Ez a számlaszám-előtag nem használható. | `validation` |
| [356](/docs/hibakodok/356) | A megadott számlaszám-előtag helyett másikat kell használni. | `validation` |
| [357](/docs/hibakodok/357) | Add meg a számlaszám-előtagot. | `validation` |
| [358](/docs/hibakodok/358) | Ezt a számlát csak a fiók tulajdonosa sztornózhatja. | `account` |
| [359](/docs/hibakodok/359) | Ez a számla megbízott számlakibocsátás keretében készült, a sztornót a megbízott végezheti. | `account` |
| [360](/docs/hibakodok/360) | Az előtag nem módosítható, mert már készült vele bizonylat. | `validation` |
| [362](/docs/hibakodok/362) | Ezt az előtagot ez az adószám korábban már használta. | `validation` |
| [363](/docs/hibakodok/363) | A tétel bruttó értékének egész számnak kell lennie. | `validation` |
| [364](/docs/hibakodok/364) | A tétel nettó értéke maximum 2 tizedes jegyet tartalmazhat. | `validation` |
| [365](/docs/hibakodok/365) | A tétel áfa értéke maximum 2 tizedes jegyet tartalmazhat. | `validation` |
| [395](/docs/hibakodok/395) | Érvénytelen áfakulcs. | `validation` |
| [396](/docs/hibakodok/396) | A megadott dátum túl korai. | `validation` |
| [489](/docs/hibakodok/489) | Ez a számla megbízott számlakibocsátás keretében készült, az ismétlést a megbízott végezheti. | `account` |
| [491](/docs/hibakodok/491) | Új kata adónem mellett vállalkozás felé nem állítható ki bizonylat. | `account` |
| [493](/docs/hibakodok/493) | Ezt a számlát csak a fiók tulajdonosa helyesbítheti. | `account` |
| [494](/docs/hibakodok/494) | Ez a számla megbízott számlakibocsátás keretében készült, a helyesbítést a megbízott végezheti. | `account` |
| [506](/docs/hibakodok/506) | A jelszó túl rövid. | `validation` |
| [507](/docs/hibakodok/507) | A jelszó túl hosszú. | `validation` |
| [520](/docs/hibakodok/520) | Nincs elegendő törlőkódod, kérlek tölts fel újat. | `account` |
| [521](/docs/hibakodok/521) | Jelenleg csak a Számlázz.hu ajánlott számlaképpel használható a törlőkód. | `validation` |
| [524](/docs/hibakodok/524) | A nyugtaszám-előtag nincs engedélyezve. | `account` |
| [537](/docs/hibakodok/537) | Egy tételhez legfeljebb 400 adattörlő kód adható. | `validation` |
| [538](/docs/hibakodok/538) | Adattörlő kód demo- és tesztfiókban nem használható. | `account` |
| [539](/docs/hibakodok/539) | Nincs bekapcsolva az adattörlő kód használata. | `account` |
| [551](/docs/hibakodok/551) | Egyszerűsített számlakép OSS vagy nem magyar adószám mellett nem használható. | `validation` |
| [552](/docs/hibakodok/552) | Egyszerűsített számlaképen legfeljebb két tétel adható meg. | `validation` |
| [553](/docs/hibakodok/553) | Egyszerűsített számlakép esetén érvénytelen adókulcs. | `validation` |
| [554](/docs/hibakodok/554) | Egyszerűsített számlaképű számla nem helyesbíthető. | `validation` |
| [555](/docs/hibakodok/555) | Egyszerűsített számlaképen a tételek áfakulcsai nem különbözhetnek. | `validation` |
| [556](/docs/hibakodok/556) | Egyszerűsített számlakép szállítólevélen és helyesbítő számlán nem használható. | `validation` |
# 1-es hibakód: Rendszerkarbantartás, kérem próbálja meg pár perc múlva.
URL: https://www.kasszajs.hu/docs/hibakodok/1
> A Számlázz.hu Számla Agent 1-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Rendszerkarbantartás, kérem próbálja meg pár perc múlva.
| | |
| --- | --- |
| Kategória | `maintenance` (Karbantartás) |
| Újrapróbálható | Igen, néhány perc múlva. |
| A kassza hibája | `SzamlazzError`, `code: 1` |
## Mikor jön elő [#mikor-jön-elő]
A Számlázz.hu átmenetileg nem érhető el.
## Tipp [#tipp]
A Számlázz.hu oldalán van karbantartás, néhány perc múlva próbáld újra.
## Mi a teendő [#mi-a-teendő]
Várj néhány percet. Lekérdezéseknél a kassza magától újrapróbál; kiállításnál a `createOnce()` később biztonságosan újrahívható. Tartós kiesésnél válts tartalék folyamatra (például kézi nyugtatömbre).
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 1) {
return utemezdKesobbre(szamla)
}
throw error
}
```
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 3-as hibakód: Sikertelen bejelentkezés.
URL: https://www.kasszajs.hu/docs/hibakodok/3
> A Számlázz.hu Számla Agent 3-as hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Sikertelen bejelentkezés.
| | |
| --- | --- |
| Kategória | `auth` (Hitelesítés) |
| Újrapróbálható | Nem. Előbb javítsd a hitelesítést. |
| A kassza hibája | `SzamlazzError`, `code: 3` |
## Mikor jön elő [#mikor-jön-elő]
A Számlázz.hu nem fogadta el a bejelentkezést, vagy egy böngészős munkamenet zavarja az Agentet.
## Tipp [#tipp]
Ellenőrizd az Agent kulcsot. Csak kisbetűs kulcsot fogad el a rendszer.
## Mi a teendő [#mi-a-teendő]
Ellenőrizd az Agent kulcsot (csak kisbetűs lehet), és a `kassza doctor` paranccsal a teljes környezetet. Ugyanazt a kérést ne küldd újra, amíg a kulcs hibás.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 3) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[135](/docs/hibakodok/135), [164](/docs/hibakodok/164)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 7-es hibakód: Hiányzó adat: ismeretlen számlaszám, rendelésszám vagy külső azonosító.
URL: https://www.kasszajs.hu/docs/hibakodok/7
> A Számlázz.hu Számla Agent 7-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Hiányzó adat: ismeretlen számlaszám, rendelésszám vagy külső azonosító.
| | |
| --- | --- |
| Kategória | `not_found` (Nem található) |
| Újrapróbálható | Nem. |
| A kassza hibája | `SzamlazzError`, `code: 7` |
## Mikor jön elő [#mikor-jön-elő]
A hivatkozott bizonylat nem létezik ezen a fiókon.
## Mi a teendő [#mi-a-teendő]
Ellenőrizd a számot vagy a rendelésszámot. A `find()` metódusok ilyenkor `null`-t adnak hibadobás helyett.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 7) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[335](/docs/hibakodok/335), [339](/docs/hibakodok/339)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 8-as hibakód: Nincs jogosultság vagy megfelelő díjcsomag a művelet elvégzéséhez.
URL: https://www.kasszajs.hu/docs/hibakodok/8
> A Számlázz.hu Számla Agent 8-as hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Nincs jogosultság vagy megfelelő díjcsomag a művelet elvégzéséhez.
| | |
| --- | --- |
| Kategória | `account` (Fiókbeállítás) |
| Újrapróbálható | Nem. Újraküldve is ugyanez lesz. |
| A kassza hibája | `SzamlazzError`, `code: 8` |
## Mikor jön elő [#mikor-jön-elő]
A fiók beállítása, előfizetése vagy jogosultsága nem engedi a műveletet.
## Tipp [#tipp]
Az Agentes nyugtakibocsátás a #free csomagban nem érhető el, vagy a felhasználó szerepköre nem engedi. Megbízottként a megbízott díjcsomagja számít.
## Mi a teendő [#mi-a-teendő]
Embernek kell a Számlázz.hu felületén rendeznie (előfizetés, beállítás, jogosultság). A kódból ezt nem lehet megoldani.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 8) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[17](/docs/hibakodok/17), [49](/docs/hibakodok/49), [54](/docs/hibakodok/54), [55](/docs/hibakodok/55), [136](/docs/hibakodok/136), [137](/docs/hibakodok/137), [250](/docs/hibakodok/250), [353](/docs/hibakodok/353)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 17-es hibakód: Nem megengedett művelet.
URL: https://www.kasszajs.hu/docs/hibakodok/17
> A Számlázz.hu Számla Agent 17-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Nem megengedett művelet.
| | |
| --- | --- |
| Kategória | `account` (Fiókbeállítás) |
| Újrapróbálható | Nem. Újraküldve is ugyanez lesz. |
| A kassza hibája | `SzamlazzError`, `code: 17` |
## Mikor jön elő [#mikor-jön-elő]
A fiók beállítása, előfizetése vagy jogosultsága nem engedi a műveletet.
## Tipp [#tipp]
Megbízotti felhasználó például nem ismételhet számlát.
## Mi a teendő [#mi-a-teendő]
Embernek kell a Számlázz.hu felületén rendeznie (előfizetés, beállítás, jogosultság). A kódból ezt nem lehet megoldani.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 17) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[8](/docs/hibakodok/8), [49](/docs/hibakodok/49), [54](/docs/hibakodok/54), [55](/docs/hibakodok/55), [136](/docs/hibakodok/136), [137](/docs/hibakodok/137), [250](/docs/hibakodok/250), [353](/docs/hibakodok/353)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 49-es hibakód: A tanúsítvány használatához jelszó szükséges.
URL: https://www.kasszajs.hu/docs/hibakodok/49
> A Számlázz.hu Számla Agent 49-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A tanúsítvány használatához jelszó szükséges.
| | |
| --- | --- |
| Kategória | `account` (Fiókbeállítás) |
| Újrapróbálható | Nem. Újraküldve is ugyanez lesz. |
| A kassza hibája | `SzamlazzError`, `code: 49` |
## Mikor jön elő [#mikor-jön-elő]
A fiók beállítása, előfizetése vagy jogosultsága nem engedi a műveletet.
## Tipp [#tipp]
Az e-számla tanúsítványához tartozó jelszót a Számlázz.hu felületén kell beállítani.
## Mi a teendő [#mi-a-teendő]
Embernek kell a Számlázz.hu felületén rendeznie (előfizetés, beállítás, jogosultság). A kódból ezt nem lehet megoldani.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 49) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[8](/docs/hibakodok/8), [17](/docs/hibakodok/17), [54](/docs/hibakodok/54), [55](/docs/hibakodok/55), [136](/docs/hibakodok/136), [137](/docs/hibakodok/137), [250](/docs/hibakodok/250), [353](/docs/hibakodok/353)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 52-es hibakód: Az áfakulcs nem értelmezhető.
URL: https://www.kasszajs.hu/docs/hibakodok/52
> A Számlázz.hu Számla Agent 52-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Az áfakulcs nem értelmezhető.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 52` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Használd a VatRate típusban felsorolt áfakulcsok egyikét.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 52) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259), [260](/docs/hibakodok/260)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 53-as hibakód: Hiányzó XML fájl.
URL: https://www.kasszajs.hu/docs/hibakodok/53
> A Számlázz.hu Számla Agent 53-as hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Hiányzó XML fájl.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 53` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Az XML-t fájlként kell elküldeni multipart/form-data kérésben.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 53) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259), [260](/docs/hibakodok/260)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 54-es hibakód: E-számla készítés nincs engedélyezve.
URL: https://www.kasszajs.hu/docs/hibakodok/54
> A Számlázz.hu Számla Agent 54-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** E-számla készítés nincs engedélyezve.
| | |
| --- | --- |
| Kategória | `account` (Fiókbeállítás) |
| Újrapróbálható | Nem. Újraküldve is ugyanez lesz. |
| A kassza hibája | `SzamlazzError`, `code: 54` |
## Mikor jön elő [#mikor-jön-elő]
A fiók beállítása, előfizetése vagy jogosultsága nem engedi a műveletet.
## Tipp [#tipp]
Az előfizetési csomag nem tartalmazza az e-számlát, vagy nincs tanúsítvány. Próbáld eInvoice: false beállítással.
## Mi a teendő [#mi-a-teendő]
Embernek kell a Számlázz.hu felületén rendeznie (előfizetés, beállítás, jogosultság). A kódból ezt nem lehet megoldani.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 54) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[8](/docs/hibakodok/8), [17](/docs/hibakodok/17), [49](/docs/hibakodok/49), [55](/docs/hibakodok/55), [136](/docs/hibakodok/136), [137](/docs/hibakodok/137), [250](/docs/hibakodok/250), [353](/docs/hibakodok/353)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 55-ös hibakód: E-számla aláírása sikertelen.
URL: https://www.kasszajs.hu/docs/hibakodok/55
> A Számlázz.hu Számla Agent 55-ös hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** E-számla aláírása sikertelen.
| | |
| --- | --- |
| Kategória | `account` (Fiókbeállítás) |
| Újrapróbálható | Nem. Újraküldve is ugyanez lesz. |
| A kassza hibája | `SzamlazzError`, `code: 55` |
## Mikor jön elő [#mikor-jön-elő]
A fiók beállítása, előfizetése vagy jogosultsága nem engedi a műveletet.
## Tipp [#tipp]
A tanúsítvány lejárt, vagy az időbélyeg szerver nem érhető el.
## Mi a teendő [#mi-a-teendő]
Embernek kell a Számlázz.hu felületén rendeznie (előfizetés, beállítás, jogosultság). A kódból ezt nem lehet megoldani.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 55) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[8](/docs/hibakodok/8), [17](/docs/hibakodok/17), [49](/docs/hibakodok/49), [54](/docs/hibakodok/54), [136](/docs/hibakodok/136), [137](/docs/hibakodok/137), [250](/docs/hibakodok/250), [353](/docs/hibakodok/353)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 56-os hibakód: A bizonylat elkészült, de az értesítő e-mail kiküldése sikertelen.
URL: https://www.kasszajs.hu/docs/hibakodok/56
> A Számlázz.hu Számla Agent 56-os hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A bizonylat elkészült, de az értesítő e-mail kiküldése sikertelen.
| | |
| --- | --- |
| Kategória | `partial_success` (Részben sikeres) |
| Újrapróbálható | Nem. A bizonylat már létezik. |
| A kassza hibája | `SzamlazzError`, `code: 56` |
## Mikor jön elő [#mikor-jön-elő]
A bizonylat elkészült, csak egy mellékes lépés (például az értesítő e-mail) maradt el.
## Tipp [#tipp]
NE állítsd ki újra a bizonylatot. Kérdezd le rendelésszám vagy külső azonosító alapján, és küldd ki az e-mailt később.
## Mi a teendő [#mi-a-teendő]
Ne állítsd ki újra. Kérdezd le a bizonylatot rendelésszámmal, és a kimaradt lépést (e-mail) pótold külön.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 56) {
const meglevo = await kassza.invoices.find({ orderNumber: szamla.orderNumber })
return meglevo
}
throw error
}
```
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 57-es hibakód: XML beolvasási hiba.
URL: https://www.kasszajs.hu/docs/hibakodok/57
> A Számlázz.hu Számla Agent 57-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** XML beolvasási hiba.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 57` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
A küldött XML nem felel meg az XSD-nek. A részleteket a hibaüzenet tartalmazza.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 57) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259), [260](/docs/hibakodok/260)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 68-as hibakód: Már létezik cég ezzel az adószámmal és számlaszám-előtaggal.
URL: https://www.kasszajs.hu/docs/hibakodok/68
> A Számlázz.hu Számla Agent 68-as hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Már létezik cég ezzel az adószámmal és számlaszám-előtaggal.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 68` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
A megbízói fiók csatlakozásához egyeztess másik megbízotti előtagot a megbízóval.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 68) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259), [260](/docs/hibakodok/260)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 71-es hibakód: Már létező rendelésszám.
URL: https://www.kasszajs.hu/docs/hibakodok/71
> A Számlázz.hu Számla Agent 71-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Már létező rendelésszám.
| | |
| --- | --- |
| Kategória | `duplicate` (Már létezik) |
| Újrapróbálható | Nem. A bizonylat valószínűleg már elkészült. |
| A kassza hibája | `SzamlazzError`, `code: 71` |
## Mikor jön elő [#mikor-jön-elő]
Ezzel a rendelésszámmal vagy hívásazonosítóval már készült bizonylat.
## Tipp [#tipp]
A fiókban be van kapcsolva a rendelésszám ismétlődés tiltása. Ez a bizonylat valószínűleg már elkészült.
## Mi a teendő [#mi-a-teendő]
Ne állítsd ki újra: kérdezd le a meglévőt. A `createOnce()` ezt magától megteszi, és a meglévő bizonylatot adja vissza.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 71) {
const meglevo = await kassza.invoices.find({ orderNumber: szamla.orderNumber })
return meglevo
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[152](/docs/hibakodok/152), [338](/docs/hibakodok/338)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 101-es hibakód: A bejelentkezési név már foglalt.
URL: https://www.kasszajs.hu/docs/hibakodok/101
> A Számlázz.hu Számla Agent 101-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A bejelentkezési név már foglalt.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 101` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Új megbízói fiók létrehozásakor másik dedikált felhasználói e-mail címet adj meg.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 101) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259), [260](/docs/hibakodok/260)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 135-ös hibakód: Számla Agent futtatásához lépj ki a Számlázz.hu rendszerből a böngészőben.
URL: https://www.kasszajs.hu/docs/hibakodok/135
> A Számlázz.hu Számla Agent 135-ös hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Számla Agent futtatásához lépj ki a Számlázz.hu rendszerből a böngészőben.
| | |
| --- | --- |
| Kategória | `auth` (Hitelesítés) |
| Újrapróbálható | Nem. Előbb javítsd a hitelesítést. |
| A kassza hibája | `SzamlazzError`, `code: 135` |
## Mikor jön elő [#mikor-jön-elő]
A Számlázz.hu nem fogadta el a bejelentkezést, vagy egy böngészős munkamenet zavarja az Agentet.
## Mi a teendő [#mi-a-teendő]
Ellenőrizd az Agent kulcsot (csak kisbetűs lehet), és a `kassza doctor` paranccsal a teljes környezetet. Ugyanazt a kérést ne küldd újra, amíg a kulcs hibás.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 135) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[3](/docs/hibakodok/3), [164](/docs/hibakodok/164)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 136-os hibakód: Bejelentkezési hiba, lépj be a Számlázz.hu rendszerébe böngészőn keresztül.
URL: https://www.kasszajs.hu/docs/hibakodok/136
> A Számlázz.hu Számla Agent 136-os hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Bejelentkezési hiba, lépj be a Számlázz.hu rendszerébe böngészőn keresztül.
| | |
| --- | --- |
| Kategória | `account` (Fiókbeállítás) |
| Újrapróbálható | Nem. Újraküldve is ugyanez lesz. |
| A kassza hibája | `SzamlazzError`, `code: 136` |
## Mikor jön elő [#mikor-jön-elő]
A fiók beállítása, előfizetése vagy jogosultsága nem engedi a műveletet.
## Tipp [#tipp]
Lejárt előfizetés vagy rendezetlen díj. Ellenőrizd a Szolgáltatáscsomagom menüpontot.
## Mi a teendő [#mi-a-teendő]
Embernek kell a Számlázz.hu felületén rendeznie (előfizetés, beállítás, jogosultság). A kódból ezt nem lehet megoldani.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 136) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[8](/docs/hibakodok/8), [17](/docs/hibakodok/17), [49](/docs/hibakodok/49), [54](/docs/hibakodok/54), [55](/docs/hibakodok/55), [137](/docs/hibakodok/137), [250](/docs/hibakodok/250), [353](/docs/hibakodok/353)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 137-es hibakód: Nincs engedély számlázási fiók létrehozására Számla Agenten keresztül.
URL: https://www.kasszajs.hu/docs/hibakodok/137
> A Számlázz.hu Számla Agent 137-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Nincs engedély számlázási fiók létrehozására Számla Agenten keresztül.
| | |
| --- | --- |
| Kategória | `account` (Fiókbeállítás) |
| Újrapróbálható | Nem. Újraküldve is ugyanez lesz. |
| A kassza hibája | `SzamlazzError`, `code: 137` |
## Mikor jön elő [#mikor-jön-elő]
A fiók beállítása, előfizetése vagy jogosultsága nem engedi a műveletet.
## Tipp [#tipp]
Az action-agent_ceg_mb interfész engedélyét a Számlázz.hu ügyfélszolgálatán kell kérni, tesztfiókra is.
## Mi a teendő [#mi-a-teendő]
Embernek kell a Számlázz.hu felületén rendeznie (előfizetés, beállítás, jogosultság). A kódból ezt nem lehet megoldani.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 137) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[8](/docs/hibakodok/8), [17](/docs/hibakodok/17), [49](/docs/hibakodok/49), [54](/docs/hibakodok/54), [55](/docs/hibakodok/55), [136](/docs/hibakodok/136), [250](/docs/hibakodok/250), [353](/docs/hibakodok/353)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 152-es hibakód: Már létező rendelésszám.
URL: https://www.kasszajs.hu/docs/hibakodok/152
> A Számlázz.hu Számla Agent 152-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Már létező rendelésszám.
| | |
| --- | --- |
| Kategória | `duplicate` (Már létezik) |
| Újrapróbálható | Nem. A bizonylat valószínűleg már elkészült. |
| A kassza hibája | `SzamlazzError`, `code: 152` |
## Mikor jön elő [#mikor-jön-elő]
Ezzel a rendelésszámmal vagy hívásazonosítóval már készült bizonylat.
## Tipp [#tipp]
A fiókban be van kapcsolva a rendelésszám ismétlődés tiltása. Ez a bizonylat valószínűleg már elkészült.
## Mi a teendő [#mi-a-teendő]
Ne állítsd ki újra: kérdezd le a meglévőt. A `createOnce()` ezt magától megteszi, és a meglévő bizonylatot adja vissza.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 152) {
const meglevo = await kassza.invoices.find({ orderNumber: szamla.orderNumber })
return meglevo
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[71](/docs/hibakodok/71), [338](/docs/hibakodok/338)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 164-es hibakód: A funkciót csak egyetlen fiókhoz hozzáférő felhasználó használhatja.
URL: https://www.kasszajs.hu/docs/hibakodok/164
> A Számlázz.hu Számla Agent 164-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A funkciót csak egyetlen fiókhoz hozzáférő felhasználó használhatja.
| | |
| --- | --- |
| Kategória | `auth` (Hitelesítés) |
| Újrapróbálható | Nem. Előbb javítsd a hitelesítést. |
| A kassza hibája | `SzamlazzError`, `code: 164` |
## Mikor jön elő [#mikor-jön-elő]
A Számlázz.hu nem fogadta el a bejelentkezést, vagy egy böngészős munkamenet zavarja az Agentet.
## Tipp [#tipp]
Használj Agent kulcsot felhasználónév és jelszó helyett. Megbízottként minden megbízóhoz külön dedikált felhasználó kell.
## Mi a teendő [#mi-a-teendő]
Ellenőrizd az Agent kulcsot (csak kisbetűs lehet), és a `kassza doctor` paranccsal a teljes környezetet. Ugyanazt a kérést ne küldd újra, amíg a kulcs hibás.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 164) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[3](/docs/hibakodok/3), [135](/docs/hibakodok/135)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 167-es hibakód: A tesztfiókban túllépted a rövid idő alatt kiállítható bizonylatok számát.
URL: https://www.kasszajs.hu/docs/hibakodok/167
> A Számlázz.hu Számla Agent 167-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A tesztfiókban túllépted a rövid idő alatt kiállítható bizonylatok számát.
| | |
| --- | --- |
| Kategória | `rate_limit` (Korlát) |
| Újrapróbálható | Csak percek múlva, automatikusan soha. |
| A kassza hibája | `SzamlazzError`, `code: 167` |
## Mikor jön elő [#mikor-jön-elő]
A tesztfiókban rövid idő alatt túl sok bizonylat készült.
## Tipp [#tipp]
Tesztfiókban legfeljebb 500 bizonylat készíthető 10 percenként. Várj néhány percet, ne próbáld újra automatikusan.
## Mi a teendő [#mi-a-teendő]
Várj néhány percet. Tömeges kiállításnál a `kassza/batch` sebességkorláttal fut, és ennél a hibánál megáll.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 167) {
return utemezdKesobbre(szamla)
}
throw error
}
```
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 200-as hibakód: Ezt az előtagot ugyanezzel az adószámmal egy másik számlázási fiók használja.
URL: https://www.kasszajs.hu/docs/hibakodok/200
> A Számlázz.hu Számla Agent 200-as hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Ezt az előtagot ugyanezzel az adószámmal egy másik számlázási fiók használja.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 200` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Válassz másik előtagot.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 200) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259), [260](/docs/hibakodok/260)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 202-es hibakód: A megadott számlaszám előtag nem megfelelő.
URL: https://www.kasszajs.hu/docs/hibakodok/202
> A Számlázz.hu Számla Agent 202-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A megadott számlaszám előtag nem megfelelő.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 202` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Csak a Beállítások / Előtagok menüpontban rögzített és engedélyezett előtag használható.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 202) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [259](/docs/hibakodok/259), [260](/docs/hibakodok/260)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 250-es hibakód: A fiók nem használható, mert a fiókgazda még nem vette birtokba, vagy a meghatalmazás nincs elfogadva.
URL: https://www.kasszajs.hu/docs/hibakodok/250
> A Számlázz.hu Számla Agent 250-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A fiók nem használható, mert a fiókgazda még nem vette birtokba, vagy a meghatalmazás nincs elfogadva.
| | |
| --- | --- |
| Kategória | `account` (Fiókbeállítás) |
| Újrapróbálható | Nem. Újraküldve is ugyanez lesz. |
| A kassza hibája | `SzamlazzError`, `code: 250` |
## Mikor jön elő [#mikor-jön-elő]
A fiók beállítása, előfizetése vagy jogosultsága nem engedi a műveletet.
## Tipp [#tipp]
Megbízotti kapcsolatnál a megbízónak birtokba kell vennie a fiókot. Könyvelői vagy aggregátor meghatalmazást a Számlázz.hu felületén kell elfogadni.
## Mi a teendő [#mi-a-teendő]
Embernek kell a Számlázz.hu felületén rendeznie (előfizetés, beállítás, jogosultság). A kódból ezt nem lehet megoldani.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 250) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[8](/docs/hibakodok/8), [17](/docs/hibakodok/17), [49](/docs/hibakodok/49), [54](/docs/hibakodok/54), [55](/docs/hibakodok/55), [136](/docs/hibakodok/136), [137](/docs/hibakodok/137), [353](/docs/hibakodok/353)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 259-es hibakód: A tétel nettó értéke nem megfelelő.
URL: https://www.kasszajs.hu/docs/hibakodok/259
> A Számlázz.hu Számla Agent 259-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A tétel nettó értéke nem megfelelő.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 259` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Használd a tételeknél a netUnitPrice vagy grossUnitPrice mezőt, és hagyd, hogy a csomag számolja ki az összegeket.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 259) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [260](/docs/hibakodok/260)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 260-as hibakód: A tétel áfa értéke nem megfelelő.
URL: https://www.kasszajs.hu/docs/hibakodok/260
> A Számlázz.hu Számla Agent 260-as hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A tétel áfa értéke nem megfelelő.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 260` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Használd a tételeknél a netUnitPrice vagy grossUnitPrice mezőt, és hagyd, hogy a csomag számolja ki az összegeket.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 260) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 261-es hibakód: A tétel bruttó értéke nem megfelelő.
URL: https://www.kasszajs.hu/docs/hibakodok/261
> A Számlázz.hu Számla Agent 261-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A tétel bruttó értéke nem megfelelő.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 261` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Használd a tételeknél a netUnitPrice vagy grossUnitPrice mezőt, és hagyd, hogy a csomag számolja ki az összegeket.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 261) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 262-es hibakód: A tétel nettó értéke nem megfelelő.
URL: https://www.kasszajs.hu/docs/hibakodok/262
> A Számlázz.hu Számla Agent 262-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A tétel nettó értéke nem megfelelő.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 262` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Használd a tételeknél a netUnitPrice vagy grossUnitPrice mezőt, és hagyd, hogy a csomag számolja ki az összegeket.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 262) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 263-as hibakód: A tétel áfa értéke nem megfelelő.
URL: https://www.kasszajs.hu/docs/hibakodok/263
> A Számlázz.hu Számla Agent 263-as hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A tétel áfa értéke nem megfelelő.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 263` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Használd a tételeknél a netUnitPrice vagy grossUnitPrice mezőt, és hagyd, hogy a csomag számolja ki az összegeket.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 263) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 264-es hibakód: A tétel bruttó értéke nem megfelelő.
URL: https://www.kasszajs.hu/docs/hibakodok/264
> A Számlázz.hu Számla Agent 264-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A tétel bruttó értéke nem megfelelő.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 264` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Használd a tételeknél a netUnitPrice vagy grossUnitPrice mezőt, és hagyd, hogy a csomag számolja ki az összegeket.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 264) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 309-es hibakód: Érvénytelen adat.
URL: https://www.kasszajs.hu/docs/hibakodok/309
> A Számlázz.hu Számla Agent 309-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Érvénytelen adat.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 309` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Ellenőrizd a hibaüzenetben megnevezett mezőt, adószámnál előtte a taxpayer.query() hívással.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 309) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 335-ös hibakód: A hivatkozott díjbekérő nem található.
URL: https://www.kasszajs.hu/docs/hibakodok/335
> A Számlázz.hu Számla Agent 335-ös hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A hivatkozott díjbekérő nem található.
| | |
| --- | --- |
| Kategória | `not_found` (Nem található) |
| Újrapróbálható | Nem. |
| A kassza hibája | `SzamlazzError`, `code: 335` |
## Mikor jön elő [#mikor-jön-elő]
A hivatkozott bizonylat nem létezik ezen a fiókon.
## Mi a teendő [#mi-a-teendő]
Ellenőrizd a számot vagy a rendelésszámot. A `find()` metódusok ilyenkor `null`-t adnak hibadobás helyett.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 335) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[7](/docs/hibakodok/7), [339](/docs/hibakodok/339)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 336-os hibakód: A nyugta előtag már számlákhoz használatban van.
URL: https://www.kasszajs.hu/docs/hibakodok/336
> A Számlázz.hu Számla Agent 336-os hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A nyugta előtag már számlákhoz használatban van.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 336` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Nyugtához olyan előtag kell, amit számlán még nem használtál.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 336) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 337-es hibakód: A nyugta előtag formátuma hibás.
URL: https://www.kasszajs.hu/docs/hibakodok/337
> A Számlázz.hu Számla Agent 337-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A nyugta előtag formátuma hibás.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 337` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Az előtag csak nagybetűt és számot tartalmazhat.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 337) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 338-as hibakód: A hívásazonosító már létezik.
URL: https://www.kasszajs.hu/docs/hibakodok/338
> A Számlázz.hu Számla Agent 338-as hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A hívásazonosító már létezik.
| | |
| --- | --- |
| Kategória | `duplicate` (Már létezik) |
| Újrapróbálható | Nem. A bizonylat valószínűleg már elkészült. |
| A kassza hibája | `SzamlazzError`, `code: 338` |
## Mikor jön elő [#mikor-jön-elő]
Ezzel a rendelésszámmal vagy hívásazonosítóval már készült bizonylat.
## Tipp [#tipp]
Ezzel a callId-val már készült nyugta, kérdezd le a meglévőt.
## Mi a teendő [#mi-a-teendő]
Ne állítsd ki újra: kérdezd le a meglévőt. A `createOnce()` ezt magától megteszi, és a meglévő bizonylatot adja vissza.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 338) {
const meglevo = await kassza.invoices.find({ orderNumber: szamla.orderNumber })
return meglevo
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[71](/docs/hibakodok/71), [152](/docs/hibakodok/152)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 339-es hibakód: A nyugtaszám nem létezik.
URL: https://www.kasszajs.hu/docs/hibakodok/339
> A Számlázz.hu Számla Agent 339-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A nyugtaszám nem létezik.
| | |
| --- | --- |
| Kategória | `not_found` (Nem található) |
| Újrapróbálható | Nem. |
| A kassza hibája | `SzamlazzError`, `code: 339` |
## Mikor jön elő [#mikor-jön-elő]
A hivatkozott bizonylat nem létezik ezen a fiókon.
## Mi a teendő [#mi-a-teendő]
Ellenőrizd a számot vagy a rendelésszámot. A `find()` metódusok ilyenkor `null`-t adnak hibadobás helyett.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 339) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[7](/docs/hibakodok/7), [335](/docs/hibakodok/335)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 340-es hibakód: A kifizetett összeg eltér a bruttó végösszegtől.
URL: https://www.kasszajs.hu/docs/hibakodok/340
> A Számlázz.hu Számla Agent 340-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A kifizetett összeg eltér a bruttó végösszegtől.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 340` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 340) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 352-es hibakód: A számla kelte csak a mai nap lehet.
URL: https://www.kasszajs.hu/docs/hibakodok/352
> A Számlázz.hu Számla Agent 352-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A számla kelte csak a mai nap lehet.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 352` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
A dátumot magyar idő (Europe/Budapest) szerint kell megadni, nem UTC szerint.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 352) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 353-as hibakód: Nincs egyetlen érvényes számlaszám-előtag sem.
URL: https://www.kasszajs.hu/docs/hibakodok/353
> A Számlázz.hu Számla Agent 353-as hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Nincs egyetlen érvényes számlaszám-előtag sem.
| | |
| --- | --- |
| Kategória | `account` (Fiókbeállítás) |
| Újrapróbálható | Nem. Újraküldve is ugyanez lesz. |
| A kassza hibája | `SzamlazzError`, `code: 353` |
## Mikor jön elő [#mikor-jön-elő]
A fiók beállítása, előfizetése vagy jogosultsága nem engedi a műveletet.
## Tipp [#tipp]
A Beállítások / Előtagok oldalon hozz létre vagy engedélyezz egy előtagot.
## Mi a teendő [#mi-a-teendő]
Embernek kell a Számlázz.hu felületén rendeznie (előfizetés, beállítás, jogosultság). A kódból ezt nem lehet megoldani.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 353) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[8](/docs/hibakodok/8), [17](/docs/hibakodok/17), [49](/docs/hibakodok/49), [54](/docs/hibakodok/54), [55](/docs/hibakodok/55), [136](/docs/hibakodok/136), [137](/docs/hibakodok/137), [250](/docs/hibakodok/250)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 354-es hibakód: Ez a számlaszám-előtag nem használható.
URL: https://www.kasszajs.hu/docs/hibakodok/354
> A Számlázz.hu Számla Agent 354-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Ez a számlaszám-előtag nem használható.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 354` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Megbízottként csak a megbízóval egyeztetett, a cégedhez kötött előtagot használhatod.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 354) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 356-os hibakód: A megadott számlaszám-előtag helyett másikat kell használni.
URL: https://www.kasszajs.hu/docs/hibakodok/356
> A Számlázz.hu Számla Agent 356-os hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A megadott számlaszám-előtag helyett másikat kell használni.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 356` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
A hibaüzenet megnevezi a használandó előtagot, a suggestedPrefix() kiolvassa.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 356) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 357-es hibakód: Add meg a számlaszám-előtagot.
URL: https://www.kasszajs.hu/docs/hibakodok/357
> A Számlázz.hu Számla Agent 357-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Add meg a számlaszám-előtagot.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 357` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Megbízotti számlázásnál mindig küldd el az egyeztetett előtagot (prefix).
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 357) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 358-as hibakód: Ezt a számlát csak a fiók tulajdonosa sztornózhatja.
URL: https://www.kasszajs.hu/docs/hibakodok/358
> A Számlázz.hu Számla Agent 358-as hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Ezt a számlát csak a fiók tulajdonosa sztornózhatja.
| | |
| --- | --- |
| Kategória | `account` (Fiókbeállítás) |
| Újrapróbálható | Nem. Újraküldve is ugyanez lesz. |
| A kassza hibája | `SzamlazzError`, `code: 358` |
## Mikor jön elő [#mikor-jön-elő]
A fiók beállítása, előfizetése vagy jogosultsága nem engedi a műveletet.
## Mi a teendő [#mi-a-teendő]
Embernek kell a Számlázz.hu felületén rendeznie (előfizetés, beállítás, jogosultság). A kódból ezt nem lehet megoldani.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 358) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[8](/docs/hibakodok/8), [17](/docs/hibakodok/17), [49](/docs/hibakodok/49), [54](/docs/hibakodok/54), [55](/docs/hibakodok/55), [136](/docs/hibakodok/136), [137](/docs/hibakodok/137), [250](/docs/hibakodok/250)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 359-es hibakód: Ez a számla megbízott számlakibocsátás keretében készült, a sztornót a megbízott végezheti.
URL: https://www.kasszajs.hu/docs/hibakodok/359
> A Számlázz.hu Számla Agent 359-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Ez a számla megbízott számlakibocsátás keretében készült, a sztornót a megbízott végezheti.
| | |
| --- | --- |
| Kategória | `account` (Fiókbeállítás) |
| Újrapróbálható | Nem. Újraküldve is ugyanez lesz. |
| A kassza hibája | `SzamlazzError`, `code: 359` |
## Mikor jön elő [#mikor-jön-elő]
A fiók beállítása, előfizetése vagy jogosultsága nem engedi a műveletet.
## Mi a teendő [#mi-a-teendő]
Embernek kell a Számlázz.hu felületén rendeznie (előfizetés, beállítás, jogosultság). A kódból ezt nem lehet megoldani.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 359) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[8](/docs/hibakodok/8), [17](/docs/hibakodok/17), [49](/docs/hibakodok/49), [54](/docs/hibakodok/54), [55](/docs/hibakodok/55), [136](/docs/hibakodok/136), [137](/docs/hibakodok/137), [250](/docs/hibakodok/250)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 360-as hibakód: Az előtag nem módosítható, mert már készült vele bizonylat.
URL: https://www.kasszajs.hu/docs/hibakodok/360
> A Számlázz.hu Számla Agent 360-as hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Az előtag nem módosítható, mert már készült vele bizonylat.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 360` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Hozz létre új számlatömböt új előtaggal.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 360) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 362-es hibakód: Ezt az előtagot ez az adószám korábban már használta.
URL: https://www.kasszajs.hu/docs/hibakodok/362
> A Számlázz.hu Számla Agent 362-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Ezt az előtagot ez az adószám korábban már használta.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 362` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Válassz másik előtagot.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 362) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 363-as hibakód: A tétel bruttó értékének egész számnak kell lennie.
URL: https://www.kasszajs.hu/docs/hibakodok/363
> A Számlázz.hu Számla Agent 363-as hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A tétel bruttó értékének egész számnak kell lennie.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 363` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Forintos nyugtán a bruttó egész szám, a nettó és az áfa legfeljebb 2 tizedes, és a nettó + áfa pontosan a bruttó.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 363) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 364-es hibakód: A tétel nettó értéke maximum 2 tizedes jegyet tartalmazhat.
URL: https://www.kasszajs.hu/docs/hibakodok/364
> A Számlázz.hu Számla Agent 364-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A tétel nettó értéke maximum 2 tizedes jegyet tartalmazhat.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 364` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Forintos nyugtán a bruttó egész szám, a nettó és az áfa legfeljebb 2 tizedes, és a nettó + áfa pontosan a bruttó.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 364) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 365-ös hibakód: A tétel áfa értéke maximum 2 tizedes jegyet tartalmazhat.
URL: https://www.kasszajs.hu/docs/hibakodok/365
> A Számlázz.hu Számla Agent 365-ös hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A tétel áfa értéke maximum 2 tizedes jegyet tartalmazhat.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 365` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Forintos nyugtán a bruttó egész szám, a nettó és az áfa legfeljebb 2 tizedes, és a nettó + áfa pontosan a bruttó.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 365) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 395-ös hibakód: Érvénytelen áfakulcs.
URL: https://www.kasszajs.hu/docs/hibakodok/395
> A Számlázz.hu Számla Agent 395-ös hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Érvénytelen áfakulcs.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 395` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Használd a VatRate típusban felsorolt áfakulcsok egyikét.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 395) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 396-os hibakód: A megadott dátum túl korai.
URL: https://www.kasszajs.hu/docs/hibakodok/396
> A Számlázz.hu Számla Agent 396-os hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A megadott dátum túl korai.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 396` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
A kelte és a teljesítés dátuma nem lehet a lezárt időszakban.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 396) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 489-es hibakód: Ez a számla megbízott számlakibocsátás keretében készült, az ismétlést a megbízott végezheti.
URL: https://www.kasszajs.hu/docs/hibakodok/489
> A Számlázz.hu Számla Agent 489-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Ez a számla megbízott számlakibocsátás keretében készült, az ismétlést a megbízott végezheti.
| | |
| --- | --- |
| Kategória | `account` (Fiókbeállítás) |
| Újrapróbálható | Nem. Újraküldve is ugyanez lesz. |
| A kassza hibája | `SzamlazzError`, `code: 489` |
## Mikor jön elő [#mikor-jön-elő]
A fiók beállítása, előfizetése vagy jogosultsága nem engedi a műveletet.
## Mi a teendő [#mi-a-teendő]
Embernek kell a Számlázz.hu felületén rendeznie (előfizetés, beállítás, jogosultság). A kódból ezt nem lehet megoldani.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 489) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[8](/docs/hibakodok/8), [17](/docs/hibakodok/17), [49](/docs/hibakodok/49), [54](/docs/hibakodok/54), [55](/docs/hibakodok/55), [136](/docs/hibakodok/136), [137](/docs/hibakodok/137), [250](/docs/hibakodok/250)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 491-es hibakód: Új kata adónem mellett vállalkozás felé nem állítható ki bizonylat.
URL: https://www.kasszajs.hu/docs/hibakodok/491
> A Számlázz.hu Számla Agent 491-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Új kata adónem mellett vállalkozás felé nem állítható ki bizonylat.
| | |
| --- | --- |
| Kategória | `account` (Fiókbeállítás) |
| Újrapróbálható | Nem. Újraküldve is ugyanez lesz. |
| A kassza hibája | `SzamlazzError`, `code: 491` |
## Mikor jön elő [#mikor-jön-elő]
A fiók beállítása, előfizetése vagy jogosultsága nem engedi a műveletet.
## Tipp [#tipp]
A fiókban bekapcsolt KATA-védelem tiltja. Hagyd el a vevő adószámát, vagy kapcsold ki a védelmet a saját felelősségedre.
## Mi a teendő [#mi-a-teendő]
Embernek kell a Számlázz.hu felületén rendeznie (előfizetés, beállítás, jogosultság). A kódból ezt nem lehet megoldani.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 491) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[8](/docs/hibakodok/8), [17](/docs/hibakodok/17), [49](/docs/hibakodok/49), [54](/docs/hibakodok/54), [55](/docs/hibakodok/55), [136](/docs/hibakodok/136), [137](/docs/hibakodok/137), [250](/docs/hibakodok/250)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 493-as hibakód: Ezt a számlát csak a fiók tulajdonosa helyesbítheti.
URL: https://www.kasszajs.hu/docs/hibakodok/493
> A Számlázz.hu Számla Agent 493-as hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Ezt a számlát csak a fiók tulajdonosa helyesbítheti.
| | |
| --- | --- |
| Kategória | `account` (Fiókbeállítás) |
| Újrapróbálható | Nem. Újraküldve is ugyanez lesz. |
| A kassza hibája | `SzamlazzError`, `code: 493` |
## Mikor jön elő [#mikor-jön-elő]
A fiók beállítása, előfizetése vagy jogosultsága nem engedi a műveletet.
## Mi a teendő [#mi-a-teendő]
Embernek kell a Számlázz.hu felületén rendeznie (előfizetés, beállítás, jogosultság). A kódból ezt nem lehet megoldani.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 493) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[8](/docs/hibakodok/8), [17](/docs/hibakodok/17), [49](/docs/hibakodok/49), [54](/docs/hibakodok/54), [55](/docs/hibakodok/55), [136](/docs/hibakodok/136), [137](/docs/hibakodok/137), [250](/docs/hibakodok/250)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 494-es hibakód: Ez a számla megbízott számlakibocsátás keretében készült, a helyesbítést a megbízott végezheti.
URL: https://www.kasszajs.hu/docs/hibakodok/494
> A Számlázz.hu Számla Agent 494-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Ez a számla megbízott számlakibocsátás keretében készült, a helyesbítést a megbízott végezheti.
| | |
| --- | --- |
| Kategória | `account` (Fiókbeállítás) |
| Újrapróbálható | Nem. Újraküldve is ugyanez lesz. |
| A kassza hibája | `SzamlazzError`, `code: 494` |
## Mikor jön elő [#mikor-jön-elő]
A fiók beállítása, előfizetése vagy jogosultsága nem engedi a műveletet.
## Mi a teendő [#mi-a-teendő]
Embernek kell a Számlázz.hu felületén rendeznie (előfizetés, beállítás, jogosultság). A kódból ezt nem lehet megoldani.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 494) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[8](/docs/hibakodok/8), [17](/docs/hibakodok/17), [49](/docs/hibakodok/49), [54](/docs/hibakodok/54), [55](/docs/hibakodok/55), [136](/docs/hibakodok/136), [137](/docs/hibakodok/137), [250](/docs/hibakodok/250)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 506-os hibakód: A jelszó túl rövid.
URL: https://www.kasszajs.hu/docs/hibakodok/506
> A Számlázz.hu Számla Agent 506-os hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A jelszó túl rövid.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 506` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
A dedikált felhasználó jelszava legalább 8 karakter legyen.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 506) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 507-es hibakód: A jelszó túl hosszú.
URL: https://www.kasszajs.hu/docs/hibakodok/507
> A Számlázz.hu Számla Agent 507-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A jelszó túl hosszú.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 507` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
A dedikált felhasználó jelszava legfeljebb 128 karakter lehet.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 507) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 520-as hibakód: Nincs elegendő törlőkódod, kérlek tölts fel újat.
URL: https://www.kasszajs.hu/docs/hibakodok/520
> A Számlázz.hu Számla Agent 520-as hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Nincs elegendő törlőkódod, kérlek tölts fel újat.
| | |
| --- | --- |
| Kategória | `account` (Fiókbeállítás) |
| Újrapróbálható | Nem. Újraküldve is ugyanez lesz. |
| A kassza hibája | `SzamlazzError`, `code: 520` |
## Mikor jön elő [#mikor-jön-elő]
A fiók beállítása, előfizetése vagy jogosultsága nem engedi a műveletet.
## Tipp [#tipp]
Tölts fel új adattörlő kódokat a Számlázz.hu fiókodba. A bizonylat nem jött létre.
## Mi a teendő [#mi-a-teendő]
Embernek kell a Számlázz.hu felületén rendeznie (előfizetés, beállítás, jogosultság). A kódból ezt nem lehet megoldani.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 520) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[8](/docs/hibakodok/8), [17](/docs/hibakodok/17), [49](/docs/hibakodok/49), [54](/docs/hibakodok/54), [55](/docs/hibakodok/55), [136](/docs/hibakodok/136), [137](/docs/hibakodok/137), [250](/docs/hibakodok/250)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 521-es hibakód: Jelenleg csak a Számlázz.hu ajánlott számlaképpel használható a törlőkód.
URL: https://www.kasszajs.hu/docs/hibakodok/521
> A Számlázz.hu Számla Agent 521-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Jelenleg csak a Számlázz.hu ajánlott számlaképpel használható a törlőkód.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 521` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Adattörlő kódot csak az alapértelmezett sablonnal kérj: számlánál az ajánlott számlaképpel (SzlaMost), nyugtánál a normál A4-es sablonnal.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 521) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 524-es hibakód: A nyugtaszám-előtag nincs engedélyezve.
URL: https://www.kasszajs.hu/docs/hibakodok/524
> A Számlázz.hu Számla Agent 524-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** A nyugtaszám-előtag nincs engedélyezve.
| | |
| --- | --- |
| Kategória | `account` (Fiókbeállítás) |
| Újrapróbálható | Nem. Újraküldve is ugyanez lesz. |
| A kassza hibája | `SzamlazzError`, `code: 524` |
## Mikor jön elő [#mikor-jön-elő]
A fiók beállítása, előfizetése vagy jogosultsága nem engedi a műveletet.
## Tipp [#tipp]
A Beállítások / Előtagok oldalon engedélyezd az előtag használatát. Meglévő nyugta sztornója ettől még működik.
## Mi a teendő [#mi-a-teendő]
Embernek kell a Számlázz.hu felületén rendeznie (előfizetés, beállítás, jogosultság). A kódból ezt nem lehet megoldani.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 524) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[8](/docs/hibakodok/8), [17](/docs/hibakodok/17), [49](/docs/hibakodok/49), [54](/docs/hibakodok/54), [55](/docs/hibakodok/55), [136](/docs/hibakodok/136), [137](/docs/hibakodok/137), [250](/docs/hibakodok/250)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 537-es hibakód: Egy tételhez legfeljebb 400 adattörlő kód adható.
URL: https://www.kasszajs.hu/docs/hibakodok/537
> A Számlázz.hu Számla Agent 537-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Egy tételhez legfeljebb 400 adattörlő kód adható.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 537` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 537) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 538-as hibakód: Adattörlő kód demo- és tesztfiókban nem használható.
URL: https://www.kasszajs.hu/docs/hibakodok/538
> A Számlázz.hu Számla Agent 538-as hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Adattörlő kód demo- és tesztfiókban nem használható.
| | |
| --- | --- |
| Kategória | `account` (Fiókbeállítás) |
| Újrapróbálható | Nem. Újraküldve is ugyanez lesz. |
| A kassza hibája | `SzamlazzError`, `code: 538` |
## Mikor jön elő [#mikor-jön-elő]
A fiók beállítása, előfizetése vagy jogosultsága nem engedi a műveletet.
## Mi a teendő [#mi-a-teendő]
Embernek kell a Számlázz.hu felületén rendeznie (előfizetés, beállítás, jogosultság). A kódból ezt nem lehet megoldani.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 538) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[8](/docs/hibakodok/8), [17](/docs/hibakodok/17), [49](/docs/hibakodok/49), [54](/docs/hibakodok/54), [55](/docs/hibakodok/55), [136](/docs/hibakodok/136), [137](/docs/hibakodok/137), [250](/docs/hibakodok/250)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 539-es hibakód: Nincs bekapcsolva az adattörlő kód használata.
URL: https://www.kasszajs.hu/docs/hibakodok/539
> A Számlázz.hu Számla Agent 539-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Nincs bekapcsolva az adattörlő kód használata.
| | |
| --- | --- |
| Kategória | `account` (Fiókbeállítás) |
| Újrapróbálható | Nem. Újraküldve is ugyanez lesz. |
| A kassza hibája | `SzamlazzError`, `code: 539` |
## Mikor jön elő [#mikor-jön-elő]
A fiók beállítása, előfizetése vagy jogosultsága nem engedi a műveletet.
## Tipp [#tipp]
A számlázási beállításokban kapcsold be az adattörlő kódot.
## Mi a teendő [#mi-a-teendő]
Embernek kell a Számlázz.hu felületén rendeznie (előfizetés, beállítás, jogosultság). A kódból ezt nem lehet megoldani.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 539) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[8](/docs/hibakodok/8), [17](/docs/hibakodok/17), [49](/docs/hibakodok/49), [54](/docs/hibakodok/54), [55](/docs/hibakodok/55), [136](/docs/hibakodok/136), [137](/docs/hibakodok/137), [250](/docs/hibakodok/250)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 551-es hibakód: Egyszerűsített számlakép OSS vagy nem magyar adószám mellett nem használható.
URL: https://www.kasszajs.hu/docs/hibakodok/551
> A Számlázz.hu Számla Agent 551-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Egyszerűsített számlakép OSS vagy nem magyar adószám mellett nem használható.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 551` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Az egyszerűsített számlakép (simpleItems) szabályait lásd a docs utazásszervezőknek szóló oldalán.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 551) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 552-es hibakód: Egyszerűsített számlaképen legfeljebb két tétel adható meg.
URL: https://www.kasszajs.hu/docs/hibakodok/552
> A Számlázz.hu Számla Agent 552-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Egyszerűsített számlaképen legfeljebb két tétel adható meg.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 552` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Az egyszerűsített számlakép (simpleItems) szabályait lásd a docs utazásszervezőknek szóló oldalán.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 552) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 553-as hibakód: Egyszerűsített számlakép esetén érvénytelen adókulcs.
URL: https://www.kasszajs.hu/docs/hibakodok/553
> A Számlázz.hu Számla Agent 553-as hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Egyszerűsített számlakép esetén érvénytelen adókulcs.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 553` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Az egyszerűsített számlakép (simpleItems) szabályait lásd a docs utazásszervezőknek szóló oldalán.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 553) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 554-es hibakód: Egyszerűsített számlaképű számla nem helyesbíthető.
URL: https://www.kasszajs.hu/docs/hibakodok/554
> A Számlázz.hu Számla Agent 554-es hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Egyszerűsített számlaképű számla nem helyesbíthető.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 554` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Az egyszerűsített számlakép (simpleItems) szabályait lásd a docs utazásszervezőknek szóló oldalán.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 554) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 555-ös hibakód: Egyszerűsített számlaképen a tételek áfakulcsai nem különbözhetnek.
URL: https://www.kasszajs.hu/docs/hibakodok/555
> A Számlázz.hu Számla Agent 555-ös hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Egyszerűsített számlaképen a tételek áfakulcsai nem különbözhetnek.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 555` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Az egyszerűsített számlakép (simpleItems) szabályait lásd a docs utazásszervezőknek szóló oldalán.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 555) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# 556-os hibakód: Egyszerűsített számlakép szállítólevélen és helyesbítő számlán nem használható.
URL: https://www.kasszajs.hu/docs/hibakodok/556
> A Számlázz.hu Számla Agent 556-os hibakódja: mit jelent, mikor jön elő, és hogyan kezeld a kasszával.
**Üzenet:** Egyszerűsített számlakép szállítólevélen és helyesbítő számlán nem használható.
| | |
| --- | --- |
| Kategória | `validation` (Hibás adat) |
| Újrapróbálható | Nem. Változatlanul újraküldve ugyanígy elbukik, és a próbálkozások a kitiltási korlátba számítanak. |
| A kassza hibája | `SzamlazzError`, `code: 556` |
## Mikor jön elő [#mikor-jön-elő]
A kérés valamelyik adata nem felel meg a Számlázz.hu szabályainak.
## Tipp [#tipp]
Az egyszerűsített számlakép (simpleItems) szabályait lásd a docs utazásszervezőknek szóló oldalán.
## Mi a teendő [#mi-a-teendő]
Javítsd az adatot a hibaüzenet és a tipp alapján. A kassza a legtöbb ilyen hibát már a kérés elküldése előtt, magyar üzenettel jelzi.
## Kezelés a kasszával [#kezelés-a-kasszával]
```ts
import { isSzamlazzError } from 'kassza'
try {
await kassza.invoices.createOnce(szamla)
} catch (error) {
if (isSzamlazzError(error) && error.code === 556) {
console.error(error.message, error.hint, error.docsUrl)
return jelezdEmbernek(error)
}
throw error
}
```
## Kapcsolódó hibakódok [#kapcsolódó-hibakódok]
[52](/docs/hibakodok/52), [53](/docs/hibakodok/53), [57](/docs/hibakodok/57), [68](/docs/hibakodok/68), [101](/docs/hibakodok/101), [200](/docs/hibakodok/200), [202](/docs/hibakodok/202), [259](/docs/hibakodok/259)
A hibakódok teljes, hivatalos listája a [Számlázz.hu dokumentációjában](https://docs.szamlazz.hu/hu/agent/basics/error-handling) van. Ez az oldal a kassza `AGENT_ERROR_CODES` táblájából készül.
# Receptek
URL: https://www.kasszajs.hu/docs/receptek
> Teljes, bemásolható integrációs minták a kasszához. Webshop számlázás, Stripe és IPN webhook, díjbekérő, nyugta, PDF tárolás, adószám-kitöltés, devizás számla, Cloudflare Workers és egységtesztek.
A receptek teljes, működő fájlok, nem kódrészletek. Next.js App Router projektre készültek, de a kassza hívásai bármilyen szerveren ugyanígy működnek. Mindegyik a gyakorlatban bevált mintát követi: rendelésszám minden bizonylaton, lekérdezés a kiállítás előtt, és soha nincs újraküldés ciklusban.
A receptek a `@/lib/kassza` modulból importálják a klienst, ezt a [Közös kliens](/docs/receptek/kozos-kliens)
recept írja le. A `@/lib/rendelesek` és a hasonló modulok a saját adatbázis-rétegedet jelölik.
Ezeket a függvényeket a saját adatbázisodhoz kell megírnod, a receptek csak a nevüket és a
szerepüket rögzítik.
## Alapok [#alapok]
Egyetlen szerveroldali kliens alapbeállításokkal, naplózással és közös munkamenettel.
Idempotens számlázás rendelésszámmal, amelyet minden fizetési webhook újrahasznosít.
## Fizetések [#fizetések]
Stripe, SimplePay, Barion, Revolut és PayPal fizetéshez kész webhookkezelő is van, amely pontosan egyszer állít ki nyugtát vagy számlát: lásd [Fizetésből bizonylat](/docs/fizetesek). Az alábbi receptek a lépéseket mutatják, saját vezérléssel.
checkout.session.completed esemény, aláírás-ellenőrzés, dupla számla nélkül.
Díjbekérő a nevezéskor, számla a befizetés után, törlés lemondáskor.
A Számlázz.hu fizetési értesítésének ellenőrzött, idempotens feldolgozása.
Food truck nyugta createOnce-szal, e-mail kiküldéssel, dupla nyugta nélkül.
## Adatok és tárolás [#adatok-és-tárolás]
A számla PDF-je saját tárhelyre, lejáró letöltési linkkel, AWS SDK nélkül.
Számlázási űrlap, amely a NAV adataiból tölti ki a cégnevet és a címet.
Euróban kiállított, angol nyelvű számla közösségi adószámmal.
## Futtatás és tesztelés [#futtatás-és-tesztelés]
Worker KV munkamenettel és R2-ben gyorsítótárazott számla PDF-ekkel.
Vitest tesztek mock klienssel: idempotencia, hálózati hiba, időtúllépés.
Integrációs tesztek a valódi kassza kódon, elveszett válasszal és karbantartással.
# Közös kliens
URL: https://www.kasszajs.hu/docs/receptek/kozos-kliens
> Egyetlen szerveroldali kassza kliens alapbeállításokkal, hibanaplózással és közös munkamenettel, amelyet a többi recept importál.
A kassza klienst egy folyamatban egyszer hozd létre, és mindenhol ugyanazt használd. Így a munkamenet süti a hívások között megmarad, az alapbeállításokat (előtag, fizetési határidő, válaszcím) pedig egy helyen tartod karban. A többi recept ezt a modult importálja `@/lib/kassza` néven.
## Telepítés és kulcs [#telepítés-és-kulcs]
```package-install
kassza server-only
```
```dotenv title=".env.local"
SZAMLAZZ_AGENT_KEY=a-te-kisbetus-agent-kulcsod
```
A `server-only` csomag build hibát ad, ha a modult véletlenül egy kliens komponens importálja. Így az Agent kulcs és a kliens biztosan a szerveren marad.
## A kliens [#a-kliens]
Hosszan futó Node.js szerveren a memóriában tárolt munkamenet elég. Vercelen és más serverless platformon a példányok nem osztoznak a memórián, ott közös tárolót adj meg.
```ts title="lib/kassza.ts"
import 'server-only'
import { createKassza } from 'kassza'
export const kassza = createKassza({
defaults: {
invoice: {
prefix: 'WEB',
paymentDueInDays: 8,
seller: {
emailReplyTo: 'szamlazas@example.hu',
emailSubject: 'Elkészült a számlád',
},
},
receipt: { prefix: 'NYGT', paymentMethod: 'bankkártya' },
},
hooks: {
onError: ({ action, attempt, error, willRetry }) => {
console.warn('Számlázz.hu hiba', {
action,
attempt,
category: error.category,
code: error.code,
willRetry,
})
},
},
})
```
```ts title="lib/kassza.ts"
import 'server-only'
import { Redis } from '@upstash/redis'
import { createKassza } from 'kassza'
import { upstashRedisCookieStore } from 'kassza/cookie-stores'
export const kassza = createKassza({
cookieStore: upstashRedisCookieStore(Redis.fromEnv(), {
prefix: 'webshop:',
timeoutMs: 300,
}),
defaults: {
invoice: {
prefix: 'WEB',
paymentDueInDays: 8,
seller: {
emailReplyTo: 'szamlazas@example.hu',
emailSubject: 'Elkészült a számlád',
},
},
receipt: { prefix: 'NYGT', paymentMethod: 'bankkártya' },
},
hooks: {
onError: ({ action, attempt, error, willRetry }) => {
console.warn('Számlázz.hu hiba', {
action,
attempt,
category: error.category,
code: error.code,
willRetry,
})
},
},
})
```
A hook csak a műveletet, a kategóriát és a kódot naplózza. A kérés XML-jét és az Agent kulcsot a hookok soha nem kapják meg, a `rawResponse` mezőt pedig szándékosan hagyjuk ki, mert vevőadat lehet benne.
## Buktatók [#buktatók]
Ha a `createKassza()` egy route handleren belül fut, minden kérés saját, üres munkamenet-tárolót
kap, és minden hívás teljes hitelesítéssel indul. Ez működik, de nagy forgalomnál érezhetően
lassabb. A klienst modulszinten hozd létre, ahogy fent.
Ne adj a változónak `NEXT_PUBLIC_` előtagot, és ne importáld a `lib/kassza.ts` modult kliens
komponensből. A kulcs csak kisbetűs lehet. A nagybetűs kulcsot a kassza a kérés elküldése előtt
`configuration` hibával elutasítja.
A `createKassza()` azonnal ellenőrzi a kulcsot. Ha a `SZAMLAZZ_AGENT_KEY` egy környezetben
hiányzik, például CI-ban vagy tesztfuttatáskor, már a `lib/kassza.ts` importja `configuration`
hibát dob. Tesztekhez állíts be egy kamu kulcsot, ahogy az [Egységtesztek](/docs/receptek/egysegtesztek)
recept mutatja.
A `WEB` számlaelőtagot a Számlázz.hu felületén, a Beállítások / Előtagok menüben kell felvenni,
különben 202-es hibát kapsz. A nyugta előtagja csak nagybetűt és számot tartalmazhat, és nem
lehet olyan, amit számlán már használtál (336, 337).
Fejlesztéshez és preview környezethez Számlázz.hu tesztfiók kulcsát használd. A tesztfiókban
10 percenként legfeljebb 500 számla készíthető, az éles kulcs pedig csak az éles környezetben
legyen beállítva.
## Kapcsolódó [#kapcsolódó]
A createKassza() összes opciója és az alapértelmezések.
Hogyan tárolja a kassza a session sütit, és milyen tárolók közül választhatsz.
A kassza futtatása serverless és edge környezetben.
A következő recept, amely ezt a klienst használja.
# Fizetett rendelés számlája
URL: https://www.kasszajs.hu/docs/receptek/fizetett-rendeles-szamla
> Idempotens számlázás rendelésszámmal. Egy rendelésről akkor is csak egy számla készül, ha a fizetési webhook többször érkezik, vagy a kérés időtúllépéssel ér véget.
A vevő kifizette a rendelést, a fizetési szolgáltató (Stripe, Barion, SimplePay) szól a szerverednek, és neked számlát kell kiállítanod. A gond az, hogy a webhook többször is megérkezhet, a Számlázz.hu pedig időtúllépés után is elkészíthette a számlát. Ez a recept egy újrahasznosítható függvényt ad, amely mindkét esetben pontosan egy számlát hagy maga után.
A minta három lépésből áll:
1. A rendelésszám a saját rendelésed azonosítójából készül, így mindig ugyanaz.
2. Kiállítás előtt a kassza rendelésszám alapján megnézi, van-e már számla.
3. Bizonytalan kimenetű hiba után újra megnézi, és ha a számla mégis elkészült, azt adja vissza.
## A számlázó függvény [#a-számlázó-függvény]
```ts title="lib/szamlazas.ts"
import {
type CreateInvoiceInput,
type InvoiceBuyer,
type InvoiceItemInput,
isSzamlazzError,
type Kassza,
type SzamlazzErrorCategory,
} from 'kassza'
import { kassza as alapKliens } from '@/lib/kassza'
export type SzamlaRendelesszammal = CreateInvoiceInput & { readonly orderNumber: string }
export interface FizetettRendeles {
readonly id: string
readonly fizetesiMod: string
readonly vevo: InvoiceBuyer
readonly tetelek: readonly InvoiceItemInput[]
}
const BIZONYTALAN = new Set([
'network',
'timeout',
'partial_success',
'duplicate',
])
async function meglevoBizonylat(
kliens: Kassza,
adatok: SzamlaRendelesszammal,
): Promise {
const talalat = await kliens.invoices.find({ orderNumber: adatok.orderNumber })
if (talalat && talalat.header.type === (adatok.type ?? 'invoice')) return talalat.header.number
return undefined
}
export async function egyszerSzamlaz(
adatok: SzamlaRendelesszammal,
kliens: Kassza = alapKliens,
): Promise {
const meglevo = await meglevoBizonylat(kliens, adatok)
if (meglevo) return meglevo
try {
const szamla = await kliens.invoices.create(adatok)
return szamla.number
} catch (error) {
if (!isSzamlazzError(error) || !BIZONYTALAN.has(error.category)) throw error
const letrejott = await meglevoBizonylat(kliens, adatok)
if (letrejott) return letrejott
throw error
}
}
export function szamlazFizetettRendelest(
rendeles: FizetettRendeles,
kliens: Kassza = alapKliens,
): Promise {
return egyszerSzamlaz(
{
orderNumber: `WEB-${rendeles.id}`,
paid: true,
paymentMethod: rendeles.fizetesiMod,
downloadPdf: false,
buyer: rendeles.vevo,
items: rendeles.tetelek,
},
kliens,
)
}
```
Az `egyszerSzamlaz()` bármilyen bizonylattípussal működik, a [nevezéses](/docs/receptek/nevezes-dijbekerovel) és a [devizás](/docs/receptek/devizas-eu-szamla) recept is ezt használja. A második paraméterben kapott kliens miatt a függvény [mock klienssel tesztelhető](/docs/receptek/egysegtesztek).
A tételeknél bruttó egységárat és áfakulcsot adj meg, az összegeket a kassza számolja ki a Számlázz.hu kerekítési szabályai szerint:
```ts title="lib/rendeles-tetelek.ts"
import type { InvoiceItemInput } from 'kassza'
export interface KosarSor {
readonly termeknev: string
readonly darab: number
readonly bruttoEgysegar: number
}
export function szamlaTetelek(kosar: readonly KosarSor[]): InvoiceItemInput[] {
return kosar.map((sor) => ({
name: sor.termeknev,
quantity: sor.darab,
grossUnitPrice: sor.bruttoEgysegar,
vat: 27,
}))
}
```
## Route handler [#route-handler]
Ezt a végpontot hívja a fizetési szolgáltató visszahívása után a saját kódod, vagy egy admin felület „Számla újrapróbálása” gombja. A `@/lib/rendelesek` a saját adatbázis-réteged: a `rendelesBetoltese()` a `FizetettRendeles` mezői mellett a `fizetve` és a `szamlaszam` mezőt is visszaadja.
```ts title="app/api/rendelesek/[id]/szamla/route.ts"
import { isSzamlazzError, type SzamlazzError } from 'kassza'
import { rendelesBetoltese, szamlaszamMentese } from '@/lib/rendelesek'
import { szamlazFizetettRendelest } from '@/lib/szamlazas'
function hibaStatusz(error: SzamlazzError): number {
if (error.category === 'validation') return 422
if (error.retryable) return 503
return 502
}
export async function POST(request: Request, { params }: { params: Promise<{ id: string }> }) {
const token = process.env.BELSO_API_TOKEN
if (!token || request.headers.get('authorization') !== `Bearer ${token}`) {
return new Response('Nincs jogosultság', { status: 401 })
}
const { id } = await params
const rendeles = await rendelesBetoltese(id)
if (!rendeles) return Response.json({ hiba: 'Nincs ilyen rendelés.' }, { status: 404 })
if (!rendeles.fizetve) {
return Response.json({ hiba: 'A rendelés még nincs kifizetve.' }, { status: 409 })
}
if (rendeles.szamlaszam) return Response.json({ szamlaszam: rendeles.szamlaszam })
try {
const szamlaszam = await szamlazFizetettRendelest(rendeles)
await szamlaszamMentese(rendeles.id, szamlaszam)
return Response.json({ szamlaszam })
} catch (error) {
if (!isSzamlazzError(error)) throw error
return Response.json(
{ hiba: error.message, tipp: error.hint, kategoria: error.category },
{ status: hibaStatusz(error) },
)
}
}
```
A saját adatbázisban tárolt számlaszám az első védvonal: ha már megvan, a végpont a Számlázz.hu-t sem hívja. A rendelésszámos lekérdezés a második, arra az esetre, ha a számla elkészült, de a mentés már nem.
## Buktatók [#buktatók]
Ne tedd az `invoices.create()` hívást saját újrapróbáló ciklusba. Egy kérést legfeljebb ötször
szabad elküldeni, a sok felesleges kérés a fiók kitiltásához vezethet. A kassza a számlakészítést
szándékosan soha nem próbálja újra.
A rendelésszám csak a rendelés azonosítójából készüljön. Időbélyeg vagy véletlen érték mellett a
lekérdezés soha nem találja meg a korábbi számlát, és minden webhook új számlát állít ki.
Ha egy rendelésszámhoz több bizonylat tartozik, például díjbekérő és számla, a `find()` a
legutóbbit adja vissza. Ezért ellenőrzi a függvény a `header.type` mezőt is. Ha egy számlát
sztornózol, és a rendelést újra számlázni kell, adj új rendelésszámot, például `WEB-5001-2`.
Ha a vevőnek van e-mail címe, a Számlázz.hu alapból elküldi neki a számlát. Ezt a
`buyer.sendEmail: false` kapcsolja ki. A `downloadPdf: false` miatt a válaszban nem jön PDF, így
a webhook gyorsabban végez. Ha a PDF-et magad is tárolod, lásd a [PDF mentése](/docs/receptek/pdf-mentes-s3-r2)
receptet.
A kelt és a teljesítés dátuma alapból a mai nap `Europe/Budapest` időzónában. A
`new Date().toISOString().slice(0, 10)` éjfél és hajnali 2 óra között a tegnapi napot adja, ami
352-es hibát okoz.
## Kapcsolódó [#kapcsolódó]
A bizonytalan kimenetű hibák és az összes hibakategória.
A rendelésszám ismétlődés tiltása és a véletlen dupla beküldés.
Ugyanez a függvény egy Stripe Checkout webhookból hívva.
A függvény tesztjei mock klienssel.
# Stripe webhook
URL: https://www.kasszajs.hu/docs/receptek/stripe-webhook
> Számla a Stripe Checkout checkout.session.completed eseményéből. Aláírás-ellenőrzés, idempotencia rendelésszámmal, lekérdezés a kiállítás előtt, és korlátozott újrapróbálás.
A vevő a Stripe Checkout oldalán fizet, a Stripe pedig `checkout.session.completed` eseményt küld a webhook végpontodra. A végpont ellenőrzi az aláírást, megkeresi a rendelést, és kiállítja a számlát. A Stripe addig küldi újra az eseményt, amíg 2xx választ nem kap, ezért a számlázásnak idempotensnek kell lennie.
A recept a [Fizetett rendelés számlája](/docs/receptek/fizetett-rendeles-szamla) recept `szamlazFizetettRendelest()` függvényét használja, amely kiállítás előtt rendelésszám alapján megkeresi a számlát.
Ez a recept a Stripe SDK-val, lépésenként mutatja a számlázást. Ugyanezt a
[`stripeWebhook()`](/docs/fizetesek/stripe) kezelő SDK nélkül, néhány sorban elvégzi: ellenőrzi
az aláírást, pontosan egyszer állít ki nyugtát vagy számlát, és visszatérítéskor sztornóz.
## Telepítés [#telepítés]
```package-install
stripe
```
```dotenv title=".env.local"
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
```
## A Checkout munkamenet [#a-checkout-munkamenet]
A webhook két dolgot vár a munkamenettől: a saját rendelésazonosítódat a `metadata` mezőben, és a vevő számlázási címét. A cím nélkül nem lehet számlát kiállítani. A `stripeTetelek()` a saját függvényed, amely a rendelés sorait a Stripe `line_items` formátumára alakítja.
```ts title="app/api/checkout/route.ts"
import Stripe from 'stripe'
import { rendelesBetoltese, stripeTetelek } from '@/lib/rendelesek'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
export async function POST(request: Request) {
const { rendelesId } = (await request.json()) as { rendelesId: string }
const rendeles = await rendelesBetoltese(rendelesId)
if (!rendeles) return Response.json({ hiba: 'Nincs ilyen rendelés.' }, { status: 404 })
const session = await stripe.checkout.sessions.create({
mode: 'payment',
line_items: stripeTetelek(rendeles),
billing_address_collection: 'required',
metadata: { rendelesId: rendeles.id },
success_url: `${process.env.SITE_URL}/rendeles/${rendeles.id}/koszonjuk`,
cancel_url: `${process.env.SITE_URL}/kosar`,
})
return Response.json({ url: session.url })
}
```
## A webhook [#a-webhook]
A `@/lib/rendelesek` a saját adatbázis-réteged. A `szamlazasiKiserlet()` megnöveli és visszaadja a rendelés számlázási kísérleteinek számát, a `kezelesreVar()` pedig emberi ellenőrzésre jelöli a rendelést.
```ts title="app/api/stripe/webhook/route.ts"
import { type InvoiceBuyer, isSzamlazzError } from 'kassza'
import Stripe from 'stripe'
import {
kezelesreVar,
rendelesBetoltese,
szamlaszamMentese,
szamlazasiKiserlet,
} from '@/lib/rendelesek'
import { szamlazFizetettRendelest } from '@/lib/szamlazas'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
const MAX_KISERLET = 3
const ORSZAGNEVEK = new Intl.DisplayNames(['hu'], { type: 'region' })
function vevoStripebol(session: Stripe.Checkout.Session): InvoiceBuyer | undefined {
const adatok = session.customer_details
const cim = adatok?.address
if (!adatok?.name || !cim?.postal_code || !cim.city || !cim.line1) return undefined
return {
name: adatok.name,
country: cim.country && cim.country !== 'HU' ? ORSZAGNEVEK.of(cim.country) : undefined,
zip: cim.postal_code,
city: cim.city,
address: [cim.line1, cim.line2].filter(Boolean).join(', '),
email: adatok.email ?? undefined,
}
}
export async function POST(request: Request) {
const signature = request.headers.get('stripe-signature')
if (!signature) return new Response('Hiányzó aláírás', { status: 400 })
const payload = await request.text()
let event: Stripe.Event
try {
event = stripe.webhooks.constructEvent(payload, signature, process.env.STRIPE_WEBHOOK_SECRET!)
} catch {
return new Response('Érvénytelen aláírás', { status: 400 })
}
if (
event.type !== 'checkout.session.completed' &&
event.type !== 'checkout.session.async_payment_succeeded'
) {
return new Response(null, { status: 200 })
}
const session = event.data.object
if (session.payment_status !== 'paid') return new Response(null, { status: 200 })
const rendelesId = session.metadata?.rendelesId
const rendeles = rendelesId ? await rendelesBetoltese(rendelesId) : null
if (!rendeles) {
console.error('Stripe fizetés ismeretlen rendeléshez', session.id)
return new Response(null, { status: 200 })
}
if (rendeles.szamlaszam) return new Response(null, { status: 200 })
const vevo = vevoStripebol(session)
if (!vevo) {
await kezelesreVar(rendeles.id, 'A Stripe munkamenetből hiányzik a számlázási cím.')
return new Response(null, { status: 200 })
}
if ((await szamlazasiKiserlet(rendeles.id)) > MAX_KISERLET) {
await kezelesreVar(rendeles.id, 'Túl sok sikertelen számlázási kísérlet.')
return new Response(null, { status: 200 })
}
try {
const szamlaszam = await szamlazFizetettRendelest({
id: rendeles.id,
fizetesiMod: 'bankkártya',
vevo,
tetelek: rendeles.tetelek,
})
await szamlaszamMentese(rendeles.id, szamlaszam)
return new Response(null, { status: 200 })
} catch (error) {
if (!isSzamlazzError(error)) throw error
if (error.retryable) return new Response('A Számlázz.hu most nem elérhető', { status: 503 })
await kezelesreVar(rendeles.id, `${error.category}: ${error.message}`)
return new Response(null, { status: 200 })
}
}
```
A válaszkódok szándékosak:
| Helyzet | Válasz | Mi történik |
| --- | --- | --- |
| Hibás vagy hiányzó aláírás | `400` | A Stripe jelzi a hibát a Dashboardon. |
| Más esemény, fizetetlen munkamenet, már számlázott rendelés | `200` | Nincs teendő. |
| Hálózati hiba, időtúllépés vagy karbantartás, és a számla nem készült el | `503` | A Stripe később újraküldi az eseményt, és a függvény újra lekérdez a kiállítás előtt. |
| Validációs, fiók- vagy egyéb végleges hiba | `200` | A rendelés emberi ellenőrzésre vár, az újraküldés nem segítene. |
| Adatbázis- vagy egyéb nem kassza hiba | `500` | A Stripe újraküldi az eseményt. |
## Buktatók [#buktatók]
A Stripe napokig újraküldheti az eseményt. Minden újraküldés egy újabb számlakészítési kérés
lehet, a Számlázz.hu pedig legfeljebb öt próbálkozást enged egy kérésre. Ezért számolja a recept
a kísérleteket, és a harmadik után emberre bízza a rendelést.
A `constructEvent()` a kérés eredeti szövegét várja, ezért a törzset a `request.text()` olvassa
be. Ha előbb JSON-ként olvasod és újra szöveggé alakítod, az aláírás nem fog egyezni. Edge
runtime alatt az aszinkron `constructEventAsync()` változatra lehet szükség, ezt a Stripe
dokumentációjában ellenőrizd.
A tételeket és az árakat a saját adatbázisodból vedd, ne a Stripe összegeiből. A Stripe az
összegeket a pénznem legkisebb egységében adja meg, a Számlázz.hu viszont forintban és
áfakulccsal várja őket. A saját rendelésed az áfakulcsot is tudja.
Egyes fizetési módoknál a `checkout.session.completed` még `unpaid` állapottal érkezik, a
sikeres fizetésről később `checkout.session.async_payment_succeeded` esemény szól. A recept
mindkettőt figyeli, és csak `paid` állapotnál számláz. Mindkét eseményt jelöld be a webhook
beállításainál.
A Checkout alapból nem kér adószámot. Ha cégeknek is értékesítesz, az adószámot a saját
pénztár oldaladon kérd be és mentsd a rendeléshez, majd add át a `buyer.taxNumber` mezőben.
Az [Adószám alapú kitöltés](/docs/receptek/adoszam-urlap) recept ehhez ad űrlapot.
## Kapcsolódó [#kapcsolódó]
Webhookkezelő Stripe SDK nélkül, nyugtával, számlával és sztornóval.
Az idempotens számlázó függvény, amelyet a webhook hív.
Mely hibák bizonytalan kimenetűek, és melyek véglegesek.
Időkorlát egy hívásra, újrapróbálás és naplózás.
A számlázó függvény tesztelése hálózat nélkül.
# Nevezés díjbekérővel
URL: https://www.kasszajs.hu/docs/receptek/nevezes-dijbekerovel
> Versenynevezés vagy rendezvényjelentkezés átutalásos fizetéssel. Díjbekérő a jelentkezéskor, számla a befizetés után, díjbekérő törlése lemondáskor.
Egy futóversenyre a jelentkezők egy űrlapon neveznek, a nevezési díjat átutalással fizetik. Nevezéskor díjbekérőt küldesz, amelyből a jelentkező látja, mennyit és meddig kell fizetnie. Amikor a pénz megérkezik, a díjbekérőre hivatkozó számlát állítasz ki. Ha valaki lemondja a nevezést, mielőtt fizetett, a díjbekérőt törlöd.
A díjbekérő és a számla ugyanazt a rendelésszámot kapja, így a kassza mindkettőt meg tudja keresni. A recept a [Fizetett rendelés számlája](/docs/receptek/fizetett-rendeles-szamla) recept `egyszerSzamlaz()` függvényére épül.
## A nevezési logika [#a-nevezési-logika]
```ts title="lib/nevezes-szamlazas.ts"
import { type InvoiceBuyer, type InvoiceItemInput, isSzamlazzError, type Kassza } from 'kassza'
import { kassza as alapKliens } from '@/lib/kassza'
import { egyszerSzamlaz } from '@/lib/szamlazas'
export interface Nevezes {
readonly id: string
readonly vevo: InvoiceBuyer
readonly tav: string
readonly nevezesiDij: number
}
export type TorlesEredmenye = 'torolve' | 'nem-talalhato' | 'mar-szamlazva'
function rendelesszam(nevezes: Nevezes): string {
return `NEV-${nevezes.id}`
}
function tetelek(nevezes: Nevezes): InvoiceItemInput[] {
return [{ name: `Nevezési díj, ${nevezes.tav}`, grossUnitPrice: nevezes.nevezesiDij, vat: 27 }]
}
export function dijbekeroKiallitasa(
nevezes: Nevezes,
kliens: Kassza = alapKliens,
): Promise {
return egyszerSzamlaz(
{
type: 'proforma',
orderNumber: rendelesszam(nevezes),
paymentMethod: 'átutalás',
paymentDueInDays: 5,
downloadPdf: false,
buyer: nevezes.vevo,
items: tetelek(nevezes),
},
kliens,
)
}
export async function szamlaBefizetesUtan(
nevezes: Nevezes,
kliens: Kassza = alapKliens,
): Promise {
const orderNumber = rendelesszam(nevezes)
const legutobbi = await kliens.invoices.find({ orderNumber })
if (legutobbi?.header.type === 'invoice') return legutobbi.header.number
return egyszerSzamlaz(
{
orderNumber,
proformaNumber: legutobbi?.header.type === 'proforma' ? legutobbi.header.number : undefined,
paid: true,
paymentMethod: 'átutalás',
buyer: nevezes.vevo,
items: tetelek(nevezes),
},
kliens,
)
}
export async function dijbekeroTorlese(
nevezes: Nevezes,
kliens: Kassza = alapKliens,
): Promise {
const orderNumber = rendelesszam(nevezes)
const legutobbi = await kliens.invoices.find({ orderNumber })
if (legutobbi?.header.type === 'invoice') return 'mar-szamlazva'
try {
await kliens.invoices.deleteProforma({ orderNumber })
return 'torolve'
} catch (error) {
if (isSzamlazzError(error) && error.isNotFound) return 'nem-talalhato'
throw error
}
}
```
A számla ugyanazt a vevőt és ugyanazokat a tételeket kapja, mint a díjbekérő. A `proformaNumber` kapcsolja össze a két bizonylatot a Számlázz.hu-ban.
## Jelentkezés [#jelentkezés]
A `@/lib/nevezesek` a saját adatbázis-réteged: a `nevezesMentese()` elmenti a jelentkezést és visszaadja `Nevezes` alakban, a `dijbekeroszamMentese()` a nevezéshez írja a díjbekérő számát.
```ts title="app/api/nevezesek/route.ts"
import { isSzamlazzError } from 'kassza'
import { isValidEmail, isValidHungarianZipCode } from 'kassza/validators'
import { dijbekeroszamMentese, nevezesMentese } from '@/lib/nevezesek'
import { dijbekeroKiallitasa } from '@/lib/nevezes-szamlazas'
const TAVOK = {
felmaraton: { nev: 'félmaraton', dij: 12_990 },
maraton: { nev: 'maraton', dij: 16_990 },
} as const
type Tav = keyof typeof TAVOK
interface NevezesiUrlap {
readonly nev?: unknown
readonly email?: unknown
readonly iranyitoszam?: unknown
readonly varos?: unknown
readonly cim?: unknown
readonly tav?: unknown
}
function szoveg(ertek: unknown): string {
return typeof ertek === 'string' ? ertek.trim() : ''
}
function ismertTav(ertek: unknown): ertek is Tav {
return typeof ertek === 'string' && Object.hasOwn(TAVOK, ertek)
}
export async function POST(request: Request) {
const urlap = (await request.json()) as NevezesiUrlap
const nev = szoveg(urlap.nev)
const email = szoveg(urlap.email)
const iranyitoszam = szoveg(urlap.iranyitoszam)
const varos = szoveg(urlap.varos)
const cim = szoveg(urlap.cim)
if (!nev || !varos || !cim || !isValidEmail(email) || !isValidHungarianZipCode(iranyitoszam)) {
return Response.json({ hiba: 'Hiányos vagy hibás számlázási adatok.' }, { status: 400 })
}
if (!ismertTav(urlap.tav)) {
return Response.json({ hiba: 'Ismeretlen táv.' }, { status: 400 })
}
const tav = TAVOK[urlap.tav]
const nevezes = await nevezesMentese({
vevo: { name: nev, email, zip: iranyitoszam, city: varos, address: cim },
tav: tav.nev,
nevezesiDij: tav.dij,
})
try {
const dijbekeroszam = await dijbekeroKiallitasa(nevezes)
await dijbekeroszamMentese(nevezes.id, dijbekeroszam)
return Response.json({ id: nevezes.id, dijbekeroszam }, { status: 201 })
} catch (error) {
if (!isSzamlazzError(error)) throw error
console.error('A díjbekérő nem készült el', nevezes.id, error.category, error.code)
return Response.json({ id: nevezes.id, dijbekeroszam: null }, { status: 201 })
}
}
```
Ha a díjbekérő nem készül el, a nevezés ettől még mentve van. Ha később újra meghívod a `dijbekeroKiallitasa()` függvényt, az a rendelésszám alapján megtalálja a mégis elkészült díjbekérőt, így a jelentkező nem kap kettőt.
## Befizetés és lemondás [#befizetés-és-lemondás]
A két végpontot a szervezők admin felülete hívja. Az `adminKeres()` a belső token ellenőrzése, a `nevezesBetoltese()`, a `szamlaszamMentese()` és a `nevezesLemondasa()` a saját adatbázis-függvényeid.
```ts title="lib/admin.ts"
export function adminKeres(request: Request): boolean {
const token = process.env.BELSO_API_TOKEN
return Boolean(token) && request.headers.get('authorization') === `Bearer ${token}`
}
```
```ts title="app/api/nevezesek/[id]/befizetes/route.ts"
import { isSzamlazzError } from 'kassza'
import { adminKeres } from '@/lib/admin'
import { szamlaBefizetesUtan } from '@/lib/nevezes-szamlazas'
import { nevezesBetoltese, szamlaszamMentese } from '@/lib/nevezesek'
export async function POST(request: Request, { params }: { params: Promise<{ id: string }> }) {
if (!adminKeres(request)) return new Response('Nincs jogosultság', { status: 401 })
const { id } = await params
const nevezes = await nevezesBetoltese(id)
if (!nevezes) return Response.json({ hiba: 'Nincs ilyen nevezés.' }, { status: 404 })
try {
const szamlaszam = await szamlaBefizetesUtan(nevezes)
await szamlaszamMentese(nevezes.id, szamlaszam)
return Response.json({ szamlaszam })
} catch (error) {
if (!isSzamlazzError(error)) throw error
return Response.json({ hiba: error.message, tipp: error.hint }, { status: 502 })
}
}
```
```ts title="app/api/nevezesek/[id]/route.ts"
import { adminKeres } from '@/lib/admin'
import { dijbekeroTorlese } from '@/lib/nevezes-szamlazas'
import { nevezesBetoltese, nevezesLemondasa } from '@/lib/nevezesek'
export async function DELETE(request: Request, { params }: { params: Promise<{ id: string }> }) {
if (!adminKeres(request)) return new Response('Nincs jogosultság', { status: 401 })
const { id } = await params
const nevezes = await nevezesBetoltese(id)
if (!nevezes) return Response.json({ hiba: 'Nincs ilyen nevezés.' }, { status: 404 })
const eredmeny = await dijbekeroTorlese(nevezes)
if (eredmeny === 'mar-szamlazva') {
return Response.json(
{ hiba: 'A nevezési díj már be van fizetve, a számlát sztornózni kell.' },
{ status: 409 },
)
}
await nevezesLemondasa(nevezes.id)
return Response.json({ eredmeny })
}
```
## Buktatók [#buktatók]
A díjbekérő nem számla, és nem kerül a NAV-hoz. Ha díjbekérőt próbálsz sztornózni, a kassza
`validation` hibát ad, mert a Számlázz.hu ilyenkor csendben visszaadná az eredeti bizonylatot. A
ki nem fizetett díjbekérőt az `invoices.deleteProforma()` törli.
A `find()` a rendelésszámhoz tartozó legutóbbi bizonylatot adja vissza. Befizetés előtt ez a
díjbekérő, ezért a recept mindig a `header.type` mezőt is nézi. Enélkül a díjbekérőt kiállított
számlának hinnéd, és a számla soha nem készülne el.
A nevezési díj a szerveren tárolt `TAVOK` táblából jön, nem a böngészőből. Az űrlapból csak azt
fogadd el, hogy melyik távra nevez a jelentkező.
A Számlázz.hu leírása szerint a rendelésszám ismétlődés tiltása bizonylattípusonként érvényes,
ezért a díjbekérő és a számla ugyanazt a rendelésszámot kaphatja. Ha a fiókodban be van kapcsolva
a tiltás, élesítés előtt tesztfiókkal is próbáld ki a teljes folyamatot.
A határidőig ki nem fizetett nevezéseket egy időzített feladat is lemondhatja a
`dijbekeroTorlese()` hívásával. A `'nem-talalhato'` eredmény azt jelenti, hogy a díjbekérőt már
korábban törölték, ez nem hiba.
## Kapcsolódó [#kapcsolódó]
Díjbekérő, előleg- és végszámla, helyesbítő számla.
A törlés kérése, válasza és hibái.
Értesítés a Számlázz.hu-tól, ha egy számla kifizetettsége megváltozik.
Az egyszerSzamlaz() függvény, amelyre ez a recept épül.
# IPN webhook
URL: https://www.kasszajs.hu/docs/receptek/ipn-webhook
> A Számlázz.hu fizetési értesítésének (IPN) fogadása Next.js route handlerben, IP-ellenőrzéssel, a számla állapotának visszaellenőrzésével és idempotens mentéssel.
Ha a Számlázz.hu-ban egy számla kifizetett összege megváltozik, például a banki egyeztetés rögzít egy átutalást, a Számlázz.hu HTTP POST kérést küld a megadott címre. Ebből tudod meg, hogy egy rendelés ki van fizetve. Az értesítés URL-jét a Számlázz.hu fiókban, a Fiók beállítások / Számlázás alapadatok oldal alján adod meg.
Az értesítést nem írja alá semmi, csak a feladó IP-címe azonosítja. Ezért a recept az értesítést csak jelzésnek veszi: a számla tényleges állapotát a kasszával kérdezi le, és azt menti.
## A route handler [#a-route-handler]
A `fizetesiAllapotMentese()` a saját adatbázis-függvényed. Felülírja a rendelés fizetési állapotát, nem hozzáad.
```ts title="app/api/szamlazz/ipn/route.ts"
import { isSzamlazzError } from 'kassza'
import { type IpnNotification, ipnOkResponse, isSzamlazzIp, readIpnNotification } from 'kassza/ipn'
import { addMoney } from 'kassza/money'
import { kassza } from '@/lib/kassza'
import { fizetesiAllapotMentese } from '@/lib/rendelesek'
export async function POST(request: Request) {
if (!isSzamlazzIp(request.headers.get('x-forwarded-for') ?? '')) {
return new Response('Tiltott', { status: 403 })
}
let ertesites: IpnNotification
try {
ertesites = await readIpnNotification(request)
} catch (error) {
if (!isSzamlazzError(error)) throw error
console.error('Értelmezhetetlen IPN értesítés', error.message)
return new Response('Hibás értesítés', { status: 400 })
}
const szamla = await kassza.invoices.find(ertesites.invoiceNumber)
if (!szamla) {
console.error('IPN értesítés ismeretlen számláról', ertesites.invoiceNumber)
return ipnOkResponse()
}
const kifizetett = addMoney(...szamla.payments.map((befizetes) => befizetes.amount))
await fizetesiAllapotMentese({
szamlaszam: szamla.header.number,
rendelesszam: szamla.header.orderNumber,
dijbekeroszam: ertesites.proformaNumber,
kifizetett,
teljesenFizetve: kifizetett >= szamla.totals.grossAmount,
})
return ipnOkResponse()
}
```
A válaszkódok a Számlázz.hu újraküldését irányítják. Csak a `200` számít sikernek, minden más válasz vagy időtúllépés után a Számlázz.hu 3 percenként újrapróbálja, legfeljebb tízszer.
| Helyzet | Válasz | Mi történik |
| --- | --- | --- |
| Nem a Számlázz.hu címéről jött | `403` | Nincs feldolgozás. |
| Hiányzik egy kötelező mező | `400` | A Számlázz.hu újraküldi, de a hiba a naplóban látszik. |
| A számla nem található | `200` | Nincs mit menteni, az újraküldés sem segítene. |
| A lekérdezés vagy az adatbázis hibát dob | `500` | A Számlázz.hu 3 perc múlva újraküldi. |
| Sikeres mentés | `200` | Kész. |
## Az értesítés mezői [#az-értesítés-mezői]
A `readIpnNotification()` elfogad `application/x-www-form-urlencoded` és `multipart/form-data` törzset is, és az összegeket számmá alakítja.
| Mező | IPN paraméter | Leírás |
| --- | --- | --- |
| `invoiceNumber` | `szlahu_szamlaszam` | A kifizetett számla száma. |
| `proformaNumber` | `szlahu_dijbekero_szama` | A díjbekérő száma, ha a számla díjbekérőből készült. |
| `orderNumber` | `szlahu_rendelesszam` | A rendelésszám, ha szerepelt a számlán. |
| `grossTotal` | `szlahu_bruttovegosszeg` | A számla bruttó végösszege. |
| `paidAmount` | `szlahu_kifizetettbrutto` | A kifizetett bruttó összeg. |
| `paymentMethod` | `szlahu_fizetesmod` | A kifizetés módja. |
| `paymentDate` | `szlahu_kifizdat` | A kifizetés dátuma. Csak akkor érkezik, ha az ügyfélszolgálat bekapcsolta. |
| `isFullyPaid` | – | `true`, ha a kifizetett összeg eléri a végösszeget. |
## Buktatók [#buktatók]
Az `isSzamlazzIp()` a fejléc jobb szélső címét vizsgálja, azt, amelyet a hozzád legközelebbi
proxy látott. Több proxy mögött add meg a `trustedProxies` opciót, Cloudflare mögött pedig
érdemes a `cf-connecting-ip` fejlécet átadni. A recept ezért sem az értesítés összegeiben bízik,
hanem a lekérdezett számlában.
Ugyanarról a számláról több értesítés is jöhet, és a Számlázz.hu számlánként csak a legfrissebbet
küldi el. Ha minden értesítésnél hozzáadnád a kifizetett összeget a korábbihoz, a rendelés
túlfizetettnek látszana. A mentés mindig a számla aktuális állapotát írja be.
Az értesítés feldolgozása csak egy lekérdezés és egy adatbázisírás. Ha ennél többet kell tenned,
például e-mailt küldeni vagy raktárkészletet foglalni, tedd sorba, és a választ ne várasd vele.
Az IPN-t a számla kifizetett összegének változása indítja. Az értesítés feldolgozásakor ne hívd
az `invoices.registerPayment()` metódust, mert az újabb változást és újabb értesítést okozhat.
## A Számlázz.hu IP-címei [#a-számlázzhu-ip-címei]
Ha a végpontot tűzfalon is korlátozod, ezeket a címeket engedélyezd:
## Kapcsolódó [#kapcsolódó]
Az IPN részletes leírása és a kassza/ipn modul.
Bejövő és kimenő forgalom, IP-címek, proxyk.
Díjbekérős folyamat, amelyben az értesítés díjbekérőszámot is hoz.
A kassza futtatása Workerben, KV munkamenettel.
# Pénztári nyugta
URL: https://www.kasszajs.hu/docs/receptek/penztari-nyugta
> Nyugta egy food truck pénztárprogramjából a receipts.createOnce metódussal, a dupla nyugta kezelésével és e-mail kiküldéssel, Next.js route handlerben. Ki adhat így nyugtát, és mi a teendő, ha a Számlázz.hu nem érhető el.
Egy food truck tabletes pénztárprogramja minden eladás után nyugtát kér. A vevő kérheti e-mailben is. A hálózat itt sem megbízható: ha a kérés elakad, a pénztáros újra megnyomja a gombot, és ebből nem lehet két nyugta.
A Számla Agent nyugtája számítógéppel előállított nyugta, nem pótolja az online pénztárgépet.
Egy helyhez kötött bolt, büfé vagy étterem online pénztárgéppel vagy e-pénztárgéppel ad nyugtát.
A mozgó vendéglátás, például egy food truck, nem pénztárgép-köteles, ezért ott a kassza
használható. A részletek a [Nyugta vagy számla?](/docs/alapok/nyugta-vagy-szamla) oldalon
vannak.
Nyugtánál a dupla kiállítást a hívásazonosító (`callId`) akadályozza meg. Ugyanazzal a `callId`-val a Számlázz.hu nem készít második nyugtát, hanem 338-as hibát ad, amelyet a kassza `duplicate` kategóriájú hibává alakít. Emiatt a kassza hálózati hiba után a nyugtát magától is újrapróbálja, ha van `callId`.
## A nyugtakészítő függvény [#a-nyugtakészítő-függvény]
A nyugta előtagját és alapértelmezett fizetési módját a [Közös kliens](/docs/receptek/kozos-kliens) `defaults.receipt` beállítása adja.
```ts title="lib/penztar.ts"
import type { Kassza, Receipt, ReceiptItemInput } from 'kassza'
import { kassza as alapKliens } from '@/lib/kassza'
export interface Eladas {
readonly id: string
readonly fizetesiMod: 'készpénz' | 'bankkártya'
readonly tetelek: readonly ReceiptItemInput[]
}
export async function nyugtaEladasrol(
eladas: Eladas,
kliens: Kassza = alapKliens,
): Promise {
const { receipt } = await kliens.receipts.createOnce(
{
orderNumber: `POS-${eladas.id}`,
paymentMethod: eladas.fizetesiMod,
downloadPdf: false,
items: eladas.tetelek,
},
{ lookupFirst: false },
)
return receipt
}
```
A `receipts.createOnce()` a rendelésszámot hívásazonosítóként is elküldi. Ha a Számlázz.hu 338-as hibát ad, vagy a kimenet bizonytalan (hálózati hiba, időtúllépés), a rendelésszám alapján megkeresi és visszaadja a már elkészült nyugtát. A Számla Agent hívásazonosító alapján nem ad lekérdezést, ezért kell a rendelésszám is.
A `lookupFirst: false` kihagyja a kiállítás előtti lekérdezést. A pénztárnál minden eladás új, így eladásonként egy kéréssel kevesebb megy el, az ismételt gombnyomást pedig a hívásazonosító így is kiszűri. A részletek a [Pontosan egyszer](/docs/alapok/pontosan-egyszer) oldalon vannak.
A tételeknél bruttó egységárat adj meg, a nettó és az áfa összegét a kassza számolja ki a nyugtákra vonatkozó kerekítéssel:
```ts title="lib/penztar-tetelek.ts"
import type { ReceiptItemInput } from 'kassza'
export const LANGOS: ReceiptItemInput = { name: 'Lángos', grossUnitPrice: 1_490, vat: 5 }
export const ASVANYVIZ: ReceiptItemInput = { name: 'Ásványvíz 0,5 l', grossUnitPrice: 590, vat: 27 }
```
Az áfakulcsot a könyvelőddel egyeztesd: vendéglátásban például a helyben készített étel 5%-os, a palackozott ital 27%-os lehet.
## Route handler [#route-handler]
A pénztárprogram az eladás mentése után hívja ezt a végpontot, opcionálisan a vevő e-mail címével. Az `eladasBetoltese()` és a `nyugtaszamMentese()` a saját adatbázis-függvényeid, az `adminKeres()` a [Nevezés díjbekérővel](/docs/receptek/nevezes-dijbekerovel#befizetés-és-lemondás) recept belső token ellenőrzése.
```ts title="app/api/penztar/eladasok/[id]/nyugta/route.ts"
import { isSzamlazzError, type Receipt } from 'kassza'
import { isValidEmail } from 'kassza/validators'
import { adminKeres } from '@/lib/admin'
import { eladasBetoltese, nyugtaszamMentese } from '@/lib/eladasok'
import { kassza } from '@/lib/kassza'
import { nyugtaEladasrol } from '@/lib/penztar'
async function nyugtaKuldese(receiptNumber: string, email: string): Promise {
try {
await kassza.receipts.send({
receiptNumber,
emails: email,
subject: `Nyugtád: ${receiptNumber}`,
})
return true
} catch (error) {
if (!isSzamlazzError(error)) throw error
console.error('A nyugta kiküldése nem sikerült', receiptNumber, error.category)
return false
}
}
export async function POST(request: Request, { params }: { params: Promise<{ id: string }> }) {
if (!adminKeres(request)) return new Response('Nincs jogosultság', { status: 401 })
const { id } = await params
const eladas = await eladasBetoltese(id)
if (!eladas) return Response.json({ hiba: 'Nincs ilyen eladás.' }, { status: 404 })
const { email } = (await request.json().catch(() => ({}))) as { email?: unknown }
if (email !== undefined && (typeof email !== 'string' || !isValidEmail(email))) {
return Response.json({ hiba: 'Érvénytelen e-mail cím.' }, { status: 400 })
}
let nyugta: Receipt
try {
nyugta = await nyugtaEladasrol(eladas)
} catch (error) {
if (!isSzamlazzError(error)) throw error
return Response.json(
{ hiba: error.message, tipp: error.hint },
{ status: error.retryable ? 503 : 422 },
)
}
await nyugtaszamMentese(eladas.id, nyugta.number)
const kikuldve = typeof email === 'string' ? await nyugtaKuldese(nyugta.number, email) : false
return Response.json({
nyugtaszam: nyugta.number,
brutto: nyugta.totals.grossAmount,
kikuldve,
})
}
```
Ha a végpont `503`-mal válaszol, a pénztáros nyugodtan újrapróbálhatja: ugyanaz a `callId` megy el, így legfeljebb a már elkészült nyugtát kapja vissza.
## Ha a Számlázz.hu nem érhető el [#ha-a-számlázzhu-nem-érhető-el]
Karbantartás (1-es hiba) vagy hosszabb hálózati hiba alatt az árusítás nem állhat le. A pénztáros ilyenkor a nyomdai nyugtatömbből ad kézi nyugtát, a pénztárprogram pedig rögzíti, melyik eladáshoz melyik tömbnyugta tartozik. Ugyanarról az eladásról később ne állíts ki Agent-nyugtát is.
A kézi nyugtákról a Számlázz.hu nem tud, ezért a napi NAV-összesítőjüket neked kell beküldeni. Ehhez ad eszközt a [Kézi nyugtatömb](/docs/nav-nyugta/kezi-nyugtatomb) oldal.
## Buktatók [#buktatók]
A rendelésszám (és vele a hívásazonosító) az eladás azonosítójából készüljön, soha ne véletlen
értékből vagy időbélyegből. Új rendelésszámmal minden újrapróbálás új nyugtát állít ki. Egy
kérést itt is legfeljebb ötször szabad elküldeni, utána embernek kell ránéznie.
A nyugta előtagja csak nagybetűt és számot tartalmazhat, és nem lehet olyan, amelyet számlán már
használtál (336, 337). A fizetési módnak nyugtán nincs beépített alapértéke: add meg a hívásban
vagy a `defaults.receipt.paymentMethod` beállításban, különben a kassza `validation` hibát ad.
A `receipts.send()` hibája nem érinti a már elkészült nyugtát, ezért a recept csak jelzi a
`kikuldve: false` mezőben. A kiküldést a kassza nem próbálja újra magától. Egy admin felületről
később újra elküldheted a nyugtaszám alapján.
2026. szeptember 1-jétől a számítógéppel előállított nyugtákról is napi összesítőt kell küldeni
a NAV-nak, 2026. december 31-ig türelmi idővel. Az Agenttel kiállított nyugtákat a Számlázz.hu a
NAV összekötés után maga jelenti, és értesít, ha ehhez a fiókban beállítás kell. Neked az
ellenőrzés marad. Nyugtát csak olyan fiókból lehet kiállítani, ahol be van állítva a NAV Online
adatkapcsolat. Lásd [NAV nyugta-adatszolgáltatás](/docs/nav-nyugta).
Forintos nyugtán a bruttó összeg egész szám, a nettó és az áfa legfeljebb két tizedesjegy, és a
kettő összege pontosan a bruttó. Ha a `grossUnitPrice` mezőt adod meg, a kassza ezt betartja,
a kézzel számolt összegek viszont könnyen 261-es vagy 363–365-ös hibát okoznak.
## Kapcsolódó [#kapcsolódó]
Ki adhat számítógéppel előállított nyugtát, és mikor kell számla.
A nyugtakérés összes mezője, a PDF sablonok és a kerekítés.
E-mail tárgy, szöveg és válaszcím.
A nyugta tételeinek kiszámítása előre, például a kijelzőhöz.
A dupla nyugta esetének tesztelése mock klienssel.
A tartalék nyugtatömb napi NAV-összesítője.
# PDF mentése S3-ba vagy R2-be
URL: https://www.kasszajs.hu/docs/receptek/pdf-mentes-s3-r2
> A számla PDF-jének mentése saját Amazon S3 vagy Cloudflare R2 tárhelyre AWS SDK nélkül, stabil kulccsal és rövid ideig érvényes letöltési linkkel.
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 tárhely [#a-tárhely]
```ts title="lib/tarhely.ts"
import 'server-only'
import { s3FetchStorage } from 'kassza/storage'
export const tarhely = s3FetchStorage({
bucket: 'szamlak',
region: 'auto',
endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
})
```
```ts title="lib/tarhely.ts"
import 'server-only'
import { s3FetchStorage } from 'kassza/storage'
export const tarhely = s3FetchStorage({
bucket: 'ceg-szamlak',
region: 'eu-central-1',
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
})
```
## A mentés [#a-mentés]
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.
```ts title="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> = {
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 {
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.
## Route handler [#route-handler]
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](/docs/receptek/nevezes-dijbekerovel#befizetés-és-lemondás) recept belső token ellenőrzése.
```ts title="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 = 300
export 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.
## Buktatók [#buktatók]
A számla kiállítása és a PDF mentése két külön lépés. Ha a feltöltés nem sikerül, csak a mentést
próbáld újra, a számlát ne állítsd ki még egyszer. A PDF bármikor újra lekérhető rendelésszám
vagy számlaszám alapján.
A számla személyes adatot tartalmaz. A bucket ne legyen nyilvános, és ne adj meg
`publicBaseUrl`-t a számlákhoz. Letöltéshez aláírt, néhány percig érvényes linket adj ki, ahogy
a `GET` metódus teszi.
Az `invoicePdfKey()` a `date` nélkül a mai napot használja, így egy hónappal később más kulcsot
adna ugyanarra a számlára. A recept ezért a bizonylat keltét adja át, és a kapott kulcsot a
rendeléshez menti. Letöltéskor a mentett kulcsot használd, ne számold újra.
Az `invoices.create()` válaszában alapból benne van a PDF a `pdf` mezőben, ha nem adsz meg
`downloadPdf: false` beállítást. Ilyenkor a `storePdf()` azonnal is hívható, és a lekérdezés
elmarad. A külön lépés akkor jobb, ha a számlázásnak gyorsnak kell lennie, például webhookban.
Ugyanezzel a felülettel működik az `s3Storage` (AWS SDK v3 klienssel), az `r2BindingStorage`
(Workers binding), a `vercelBlobStorage`, a `supabaseStorage`, az `uploadthingStorage`, a
`kassza/storage/fs` modul `fsStorage` adaptere és a tesztekhez való `memoryStorage`.
## Kapcsolódó [#kapcsolódó]
Az összes tárhely adapter, a kulcsképzés és a StorageError.
A PDF lekérése számlaszám, rendelésszám vagy külső azonosító alapján.
PDF gyorsítótár R2 bindinggal, Workerben.
A számlázás, amely után a PDF mentése jön.
# Adószám alapú kitöltés
URL: https://www.kasszajs.hu/docs/receptek/adoszam-urlap
> Számlázási űrlap, amely a beírt adószám alapján a NAV adataiból tölti ki a cégnevet és a székhely címét. Route handler és kliens komponens.
Céges vásárlásnál a vevő beírja az adószámát, és a pénztár oldal kitölti helyette a cégnevet és a székhely címét. Kevesebb elgépelés, és a számlán a NAV-nál nyilvántartott adatok szerepelnek. A lekérdezést a kassza `taxpayer.query()` metódusa végzi a Számla Agenten keresztül.
A recept egy szerveroldali route handlerből áll, amely az Agent kulccsal lekérdez, és egy kliens komponensből, amely a formátumot már a böngészőben ellenőrzi. A kettő egy közös típuson osztozik.
## Közös típus [#közös-típus]
A `CegAdatok` az űrlap és a végpont közös adatszerkezete, a `cegesVevo()` pedig a mentett adatokból a számla vevőjét készíti el.
```ts title="lib/ceges-vevo.ts"
import type { InvoiceBuyer } from 'kassza'
export interface CegAdatok {
readonly nev: string
readonly adoszam: string
readonly iranyitoszam: string
readonly varos: string
readonly cim: string
}
export function cegesVevo(adatok: CegAdatok, email: string): InvoiceBuyer {
return {
name: adatok.nev,
zip: adatok.iranyitoszam,
city: adatok.varos,
address: adatok.cim,
email,
taxNumber: adatok.adoszam,
taxpayerType: 'hungarianTaxNumber',
}
}
```
## Route handler [#route-handler]
```ts title="app/api/adoszam/route.ts"
import { isSzamlazzError } from 'kassza'
import { parseHungarianTaxNumber } from 'kassza/validators'
import { bejelentkezettVevo } from '@/lib/auth'
import type { CegAdatok } from '@/lib/ceges-vevo'
import { kassza } from '@/lib/kassza'
export async function POST(request: Request) {
if (!(await bejelentkezettVevo(request))) return new Response('Jelentkezz be', { status: 401 })
const { adoszam } = (await request.json().catch(() => ({}))) as { adoszam?: unknown }
const ervenyes = typeof adoszam === 'string' ? parseHungarianTaxNumber(adoszam) : undefined
if (!ervenyes) return Response.json({ hiba: 'Érvénytelen adószám.' }, { status: 400 })
try {
const ceg = await kassza.taxpayer.query(ervenyes.formatted)
if (!ceg.valid || !ceg.address) {
return Response.json({ hiba: 'A NAV nem ismer ilyen érvényes adószámot.' }, { status: 404 })
}
const adatok: CegAdatok = {
nev: ceg.name ?? '',
adoszam: ceg.taxNumber?.formatted ?? ervenyes.formatted,
iranyitoszam: ceg.address.postalCode,
varos: ceg.address.city,
cim: ceg.address.formatted.split(', ').slice(1).join(', '),
}
return Response.json(adatok)
} catch (error) {
if (isSzamlazzError(error) && error.retryable) {
return Response.json({ hiba: 'A NAV lekérdezés most nem elérhető.' }, { status: 503 })
}
throw error
}
}
```
A `formatted` cím így néz ki: `1134 Budapest, Váci út 10.`. Az első vessző előtti rész az irányítószám és a település, a többi az utca és a házszám.
## Kliens komponens [#kliens-komponens]
A `kassza/validators` modul tiszta függvényekből áll, hálózatot és kulcsot nem használ, ezért a böngészőben is importálható. A `kassza` fő modult viszont soha ne importáld kliens komponensből.
```tsx title="app/penztar/szamlazasi-adatok.tsx"
'use client'
import { isValidHungarianTaxNumber } from 'kassza/validators'
import { type ChangeEvent, useState } from 'react'
import type { CegAdatok } from '@/lib/ceges-vevo'
const URES: CegAdatok = { nev: '', adoszam: '', iranyitoszam: '', varos: '', cim: '' }
type Eredmeny = CegAdatok | { readonly hiba: string }
async function cegLekerdezese(adoszam: string): Promise {
try {
const valasz = await fetch('/api/adoszam', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ adoszam }),
})
return (await valasz.json()) as Eredmeny
} catch {
return { hiba: 'A lekérdezés nem sikerült.' }
}
}
export function SzamlazasiAdatok() {
const [adatok, setAdatok] = useState(URES)
const [uzenet, setUzenet] = useState('')
const [tolt, setTolt] = useState(false)
const mezo = (nev: keyof CegAdatok) => ({
name: nev,
value: adatok[nev],
onChange: (esemeny: ChangeEvent) =>
setAdatok((elozo) => ({ ...elozo, [nev]: esemeny.target.value })),
})
async function kitoltes() {
if (!isValidHungarianTaxNumber(adatok.adoszam)) {
setUzenet('Az adószám formátuma: 12345676-1-42')
return
}
setTolt(true)
setUzenet('')
try {
const eredmeny = await cegLekerdezese(adatok.adoszam)
if ('hiba' in eredmeny) {
setUzenet(`${eredmeny.hiba} Töltsd ki kézzel az adatokat.`)
return
}
setAdatok(eredmeny)
setUzenet('Az adatokat a NAV nyilvántartásából töltöttük ki. Ellenőrizd őket.')
} finally {
setTolt(false)
}
}
return (
)
}
```
A kitöltött mezők szerkeszthetők maradnak. A NAV adatai a székhelyre vonatkoznak, de a vevő kérheti, hogy a számlán más cím szerepeljen.
## Buktatók [#buktatók]
Minden lekérdezés a te Agent kulcsoddal fut. Védelem nélkül a végpontot bárki használhatná
ingyenes NAV lekérdezőként, és a sok kérés a fiókodra hullik vissza. Csak bejelentkezett vevőnek
engedd, és korlátozd a kérések számát.
Ha a NAV nem ismeri az adószámot, vagy az nem érvényes, a `query()` nem dob hibát, hanem
`valid: false` értéket ad vissza üres címlistával. Ezt mindig ellenőrizd a mezők használata előtt.
A kassza a település és az utca nevét olvashatóvá alakítja, de a cégnevet úgy adja vissza, ahogy
a NAV tárolja, ez gyakran csupa nagybetű. Ha rövidebb nevet szeretnél, a `shortName` mező a cég
rövid nevét tartalmazza.
Az `isValidHungarianTaxNumber()` a CDV ellenőrzőszámot, az áfakódot és a megyekódot vizsgálja.
Azt, hogy a cég létezik és működik, csak a NAV lekérdezés mondja meg. A `query()` a 8 jegyű
törzsszámot is elfogadja, a számlára viszont a teljes, 11 jegyű adószám kell.
Ha az adószám áfakódja 4, a cég áfacsoport tagja. Ezt az `isHungarianVatGroupMemberTaxNumber()`
jelzi. A csoportazonosítót a `buyer.groupTaxNumber` mezőben adhatod át; hogy kell-e, azt a
könyvelőddel egyeztesd. A részletek a [Validátorok](/docs/kiegeszitok/validatorok) oldalon vannak.
## Kapcsolódó [#kapcsolódó]
A queryTaxpayer kérés, a válasz mezői és a címek feldolgozása.
Adószám, EU adószám, bankszámlaszám, irányítószám és e-mail ellenőrzése.
A céges vevő adószámának átadása a számlára.
Az InvoiceBuyer használata a számlázásban.
# Devizás számla EU-s cégnek
URL: https://www.kasszajs.hu/docs/receptek/devizas-eu-szamla
> Euróban kiállított, angol nyelvű számla másik tagállambeli cégnek, közösségi adószámmal, MNB árfolyammal és angol értesítő e-maillel.
Egy magyar fejlesztőcég egy német ügyfélnek dolgozik, és havonta euróban számlázza a ledolgozott órákat. A számla angol nyelvű, a vevő közösségi adószámmal szerepel rajta, az áfát pedig a vevő fizeti meg a saját országában. Az árfolyamot a Számlázz.hu az MNB-től veszi, ha nem adsz meg mást.
A recept a [Fizetett rendelés számlája](/docs/receptek/fizetett-rendeles-szamla) recept `egyszerSzamlaz()` függvényét használja, így a havi számla egy megbízáshoz csak egyszer készül el.
## A számlázó függvény [#a-számlázó-függvény]
```ts title="lib/eu-szamla.ts"
import type { InvoiceLanguage, Kassza } from 'kassza'
import { isValidEuVatNumber, normalizeEuVatNumber } from 'kassza/validators'
import { kassza as alapKliens } from '@/lib/kassza'
import { egyszerSzamlaz } from '@/lib/szamlazas'
export interface EuMegbizas {
readonly id: string
readonly cegnev: string
readonly orszag: string
readonly iranyitoszam: string
readonly varos: string
readonly cim: string
readonly kozossegiAdoszam: string
readonly email: string
readonly nyelv: InvoiceLanguage
readonly szolgaltatas: string
readonly orak: number
readonly oradijEur: number
}
export async function euSzolgaltatasSzamla(
megbizas: EuMegbizas,
kliens: Kassza = alapKliens,
): Promise {
const euTaxNumber = normalizeEuVatNumber(megbizas.kozossegiAdoszam)
if (!isValidEuVatNumber(euTaxNumber)) {
throw new Error(`Érvénytelen közösségi adószám: ${megbizas.kozossegiAdoszam}`)
}
if (euTaxNumber.startsWith('HU')) {
throw new Error('Magyar vevőre ez a recept nem vonatkozik.')
}
return egyszerSzamlaz(
{
orderNumber: `EU-${megbizas.id}`,
currency: 'EUR',
language: megbizas.nyelv,
paymentDueInDays: 15,
seller: {
emailSubject: 'Your invoice',
emailText: 'Please find your invoice attached. Thank you for your business.',
},
buyer: {
name: megbizas.cegnev,
country: megbizas.orszag,
zip: megbizas.iranyitoszam,
city: megbizas.varos,
address: megbizas.cim,
email: megbizas.email,
euTaxNumber,
taxpayerType: 'euBusiness',
},
items: [
{
name: megbizas.szolgaltatas,
quantity: megbizas.orak,
unit: 'hour',
netUnitPrice: megbizas.oradijEur,
vat: 'EUFAD37',
},
],
},
kliens,
)
}
```
A hívás így néz ki egy havi elszámolásból:
```ts title="scripts/havi-szamlak.ts"
import { euSzolgaltatasSzamla } from '@/lib/eu-szamla'
const szamlaszam = await euSzolgaltatasSzamla({
id: '2026-09-HAFENBLICK',
cegnev: 'Hafenblick Software GmbH',
orszag: 'Germany',
iranyitoszam: '20457',
varos: 'Hamburg',
cim: 'Am Sandtorkai 12',
kozossegiAdoszam: 'DE 123 456 789',
email: 'billing@example.de',
nyelv: 'en',
szolgaltatas: 'Software development, September 2026',
orak: 42.5,
oradijEur: 95,
})
console.log(`Kiállítva: ${szamlaszam}`)
```
A nettó egységárból a kassza számolja ki a tételösszegeket, devizában két tizedesjegyre kerekítve. Az `EUFAD37` kódos áfakulcs áfája 0, így a bruttó összeg megegyezik a nettóval.
## Árfolyam [#árfolyam]
Ha az `exchangeRate` mezőt elhagyod, az árfolyamot jegyző bank `MNB` lesz, és a Számlázz.hu az aznapi MNB árfolyamot használja. Ha a szerződésed más bank árfolyamát írja elő, az `egyszerSzamlaz()` hívásban add meg az `exchangeBank` mezőt (például `'OTP'`) és az `exchangeRate` mezőt is. Árfolyam nélkül a kassza más bankot nem fogad el, és a kérés elküldése előtt `validation` hibát ad.
## Buktatók [#buktatók]
Az `EUFAD37` az Áfa tv. 37. §-a alapján másik tagállamban teljesített, fordítottan adózó
ügyletet jelöli, ez jellemzően szolgáltatás. Termékértékesítésnél más kód kell, például `EUT`, magánszemély vevőnél
pedig egészen más szabályok érvényesek. A kassza ebben nem dönthet helyetted.
Az `isValidEuVatNumber()` csak az országkódot és a számformátumot ellenőrzi. Azt, hogy az
adószám él-e, az EU VIES rendszerében kell megnézned, a kassza ezt nem végzi el.
A `language: 'en'` a számlakép feliratait fordítja le, a tételek nevét és az e-mail szövegét
nem. Ha a [Közös kliens](/docs/receptek/kozos-kliens) alapbeállításaiban magyar e-mail tárgy van,
azt a hívás `seller` mezőjében írd felül, ahogy a recept teszi.
Forintos számlán az `exchangeRate` és az `exchangeBank` nem adható meg, a kassza ilyenkor
`validation` hibát ad. Devizás számlán az `MNB`-n kívüli bankhoz az árfolyam kötelező.
A `country` mező szabad szöveg, így kerül a számlára. Írd ki az ország nevét, például
`Germany`, ne csak az ISO kódját.
## Kapcsolódó [#kapcsolódó]
Pénznemkódok, árfolyam és árfolyamot jegyző bank.
A kódos áfakulcsok jelentése és a közösségi áfa.
Hogyan kerekít a kassza forintban és devizában.
Közösségi adószám formátumának ellenőrzése.
# Cloudflare Workers
URL: https://www.kasszajs.hu/docs/receptek/cloudflare-workers
> Cloudflare Worker, amely a kasszával lekérdezi a számlákat, a munkamenetet KV-ben tartja, a számla PDF-eket pedig R2-ben gyorsítótárazza.
A belső rendszereid (admin felület, ügyfélszolgálati eszköz, könyvelői export) egy helyről szeretnék elérni a számlákat. Ez a Worker két végpontot ad: a rendelésszámhoz tartozó számla adatait, és a számla PDF-jét. A PDF-et első lekéréskor R2-be menti, utána onnan szolgálja ki.
A Worker példányai nem osztoznak a memórián, ezért a munkamenet sütit KV-ben tartjuk. Így a kliens kérésenként is létrehozható, a munkamenet mégis megmarad.
## Beállítás [#beállítás]
```bash
npm install kassza
npx wrangler kv namespace create KASSZA_KV
npx wrangler r2 bucket create szamlak
npx wrangler secret put SZAMLAZZ_AGENT_KEY
npx wrangler secret put BELSO_API_TOKEN
npx wrangler types
```
```json title="wrangler.jsonc"
{
"name": "kassza-szamlak",
"main": "src/index.ts",
"compatibility_date": "2026-09-01",
"kv_namespaces": [{ "binding": "KASSZA_KV", "id": "a-kv-namespace-azonositoja" }],
"r2_buckets": [{ "binding": "SZAMLAK", "bucket_name": "szamlak" }]
}
```
A KV namespace azonosítóját a `wrangler kv namespace create` parancs írja ki. A `KVNamespace`, `R2Bucket`, `ExecutionContext` és `ExportedHandler` típusokat a `wrangler types` generálja. Helyi fejlesztéshez a titkokat a `.dev.vars` fájlba írd, a tesztfiók Agent kulcsával.
## A Worker [#a-worker]
```ts title="src/index.ts"
import { createKassza, isSzamlazzError, type Kassza } from 'kassza'
import { cloudflareKvCookieStore } from 'kassza/cookie-stores'
import { r2BindingStorage, sanitizeKeySegment, storePdf } from 'kassza/storage'
interface Env {
readonly SZAMLAZZ_AGENT_KEY: string
readonly BELSO_API_TOKEN: string
readonly KASSZA_KV: KVNamespace
readonly SZAMLAK: R2Bucket
}
const PDF_FEJLECEK = { 'content-type': 'application/pdf', 'cache-control': 'private, no-store' }
function kasszaKliens(env: Env): Kassza {
return createKassza({
agentKey: env.SZAMLAZZ_AGENT_KEY,
cookieStore: cloudflareKvCookieStore(env.KASSZA_KV, { prefix: 'kassza:', timeoutMs: 300 }),
})
}
function jogosult(request: Request, env: Env): boolean {
return (
Boolean(env.BELSO_API_TOKEN) &&
request.headers.get('authorization') === `Bearer ${env.BELSO_API_TOKEN}`
)
}
async function szamlaAdatai(kassza: Kassza, rendelesszam: string): Promise {
const szamla = await kassza.invoices.find({ orderNumber: rendelesszam })
if (!szamla) return Response.json({ hiba: 'Nincs ilyen számla.' }, { status: 404 })
return Response.json({
szamlaszam: szamla.header.number,
tipus: szamla.header.type,
kelt: szamla.header.issueDate,
brutto: szamla.totals.grossAmount,
penznem: szamla.header.currency,
sztornozva: szamla.header.reversed ?? false,
})
}
async function szamlaPdf(
kassza: Kassza,
env: Env,
ctx: ExecutionContext,
szamlaszam: string,
): Promise {
const tarhely = r2BindingStorage(env.SZAMLAK, { prefix: 'szamlak' })
const kulcs = `${sanitizeKeySegment(szamlaszam)}.pdf`
const mentett = await tarhely.get(kulcs)
if (mentett) return new Response(mentett, { headers: PDF_FEJLECEK })
const { pdf } = await kassza.invoices.getPdf(szamlaszam)
ctx.waitUntil(
storePdf(tarhely, kulcs, pdf).catch((error: unknown) => {
console.error('A PDF nem került az R2-be', szamlaszam, error)
}),
)
return new Response(pdf, { headers: PDF_FEJLECEK })
}
export default {
async fetch(request, env, ctx): Promise {
if (!jogosult(request, env)) return new Response('Nincs jogosultság', { status: 401 })
if (request.method !== 'GET') return new Response('Nem támogatott', { status: 405 })
const { pathname } = new URL(request.url)
const kassza = kasszaKliens(env)
try {
const rendeles = /^\/rendelesek\/([^/]+)\/szamla$/.exec(pathname)
if (rendeles?.[1]) return await szamlaAdatai(kassza, decodeURIComponent(rendeles[1]))
const pdf = /^\/szamlak\/([^/]+)\.pdf$/.exec(pathname)
if (pdf?.[1]) return await szamlaPdf(kassza, env, ctx, decodeURIComponent(pdf[1]))
return new Response('Nincs ilyen útvonal', { status: 404 })
} catch (error) {
if (!isSzamlazzError(error)) throw error
if (error.isNotFound) return Response.json({ hiba: 'Nincs ilyen számla.' }, { status: 404 })
return Response.json(
{ hiba: error.message, kategoria: error.category },
{ status: error.retryable ? 503 : 502 },
)
}
},
} satisfies ExportedHandler
```
A két végpont:
| Kérés | Válasz |
| --- | --- |
| `GET /rendelesek/WEB-5001/szamla` | A rendelésszámhoz tartozó legutóbbi bizonylat adatai JSON-ben. |
| `GET /szamlak/WEB-2026-128.pdf` | A számla PDF-je, első alkalommal a Számlázz.hu-tól, utána az R2-ből. |
## Buktatók [#buktatók]
A kassza magától a `process.env.SZAMLAZZ_AGENT_KEY` változót olvasná, ami Workersben nem
feltétlenül létezik. A kulcsot ezért az `env` objektumból, az `agentKey` opcióval add át,
különben `configuration` hibát kapsz.
Egy kiállított számla PDF-je nem változik, ezért a számlaszám biztonságos kulcs. Egy
rendelésszámhoz viszont idővel több bizonylat is tartozhat, például díjbekérő, majd számla, és a
`find()` mindig a legutóbbit adja vissza. Rendelésszám szerinti kulccsal a régi PDF ragadna a
gyorsítótárban.
A `createKassza()` nem indít hálózati kérést, ezért kérésenként is létrehozható. A munkamenet
süti a KV-ben van, így minden kliens ugyanazt használja. A KV legalább 60 másodperces lejáratot
fogad el, ezt az adapter betartja, és ha a KV nem válaszol 300 ms-on belül, a kassza munkamenet
nélkül küldi el a kérést.
A `ctx.waitUntil()` miatt a PDF feltöltése a válasz elküldése után is folytatódik. Ha a feltöltés
nem sikerül, csak a napló jelzi, a következő lekérés újra megpróbálja.
Ez a Worker csak olvas. Ha számlát is kiállítanál belőle, a végpontot ugyanúgy védd tokennel,
és kövesd a [Fizetett rendelés számlája](/docs/receptek/fizetett-rendeles-szamla) recept
rendelésszámos mintáját.
## Kapcsolódó [#kapcsolódó]
A kassza futtatása Workersben, Vercelen és más edge környezetben.
A Cloudflare KV és a többi munkamenet-tároló.
Az r2BindingStorage és a többi tárhely adapter.
Ugyanez Node.js szerveren, az R2 S3 API-ján keresztül.
# Egységtesztek
URL: https://www.kasszajs.hu/docs/receptek/egysegtesztek
> Vitest tesztek a receptek kódjához mock klienssel. Idempotencia, időtúllépés utáni lekérdezés, díjbekérő és számla, dupla nyugta, budapesti dátum és egy route handler teszt.
A receptek függvényei második paraméterben kapják a klienst, ezért tesztben a `kassza/testing` mock kliensét adhatod át nekik. A mock nem hív hálózatot, ugyanazt a validációt és kerekítést futtatja, mint az éles kliens, és naplózza a hívásokat. Ez a recept a többi recept kódjához ad kész teszteket.
A mock részletes leírása a [Tesztelés](/docs/kiegeszitok/teszteles) oldalon van. Itt azt látod, mit érdemes tesztelni egy valódi integrációban.
## Beállítás [#beállítás]
```package-install
-D vitest vite-tsconfig-paths
```
```ts title="vitest.config.ts"
import tsconfigPaths from 'vite-tsconfig-paths'
import { defineConfig } from 'vitest/config'
export default defineConfig({
plugins: [tsconfigPaths()],
test: {
env: { SZAMLAZZ_AGENT_KEY: 'teszt-kulcs-nem-valodi' },
},
})
```
A `tsconfigPaths()` miatt a tesztekben is működnek a `@/lib/...` importok. A kamu kulcsra azért van szükség, mert a receptek modulszinten importálják a `@/lib/kassza` klienst, a `createKassza()` pedig már létrehozáskor ellenőrzi a kulcs meglétét.
## A számlázó függvény [#a-számlázó-függvény]
A [Fizetett rendelés számlája](/docs/receptek/fizetett-rendeles-szamla) recept `szamlazFizetettRendelest()` függvényének tesztjei.
```ts title="lib/szamlazas.test.ts"
import { type Kassza, SzamlazzError } from 'kassza'
import { createMockKassza, type MockKassza } from 'kassza/testing'
import { expect, test } from 'vitest'
import { type FizetettRendeles, szamlazFizetettRendelest } from './szamlazas'
const rendeles: FizetettRendeles = {
id: '5001',
fizetesiMod: 'bankkártya',
vevo: {
name: 'Nagy Péter',
zip: '1111',
city: 'Budapest',
address: 'Fő utca 1.',
email: 'peter@example.hu',
},
tetelek: [{ name: 'Póló', quantity: 2, grossUnitPrice: 5_990, vat: 27 }],
}
function szamlakeszitesek(mock: MockKassza): number {
return mock.calls.filter((hivas) => hivas.method === 'invoices.create').length
}
function idotullepesUtanElkeszul(mock: MockKassza): Kassza {
return {
...mock,
invoices: {
...mock.invoices,
create: async (adatok) => {
await mock.invoices.create(adatok)
throw new SzamlazzError('A Számlázz.hu nem válaszolt.', { category: 'timeout' })
},
},
}
}
test('egy rendelésről csak egy számla készül', async () => {
const mock = createMockKassza()
const elso = await szamlazFizetettRendelest(rendeles, mock)
const masodik = await szamlazFizetettRendelest(rendeles, mock)
expect(masodik).toBe(elso)
expect(szamlakeszitesek(mock)).toBe(1)
expect(mock.invoiceRecords.get(elso)?.details.totals.grossAmount).toBe(11_980)
})
test('időtúllépés után a mégis elkészült számlát adja vissza', async () => {
const mock = createMockKassza()
const szamlaszam = await szamlazFizetettRendelest(rendeles, idotullepesUtanElkeszul(mock))
expect(mock.invoiceRecords.size).toBe(1)
expect(mock.invoiceRecords.has(szamlaszam)).toBe(true)
})
test('hálózati hibát továbbdob, ha nem készült számla', async () => {
const mock = createMockKassza()
mock.failNext('invoices.create')
await expect(szamlazFizetettRendelest(rendeles, mock)).rejects.toMatchObject({
category: 'network',
})
expect(mock.invoiceRecords.size).toBe(0)
})
test('validációs hiba után nem kérdez le újra', async () => {
const mock = createMockKassza()
const szamlazas = szamlazFizetettRendelest({ ...rendeles, tetelek: [] }, mock)
await expect(szamlazas).rejects.toMatchObject({ category: 'validation' })
expect(mock.calls.map((hivas) => hivas.method)).toEqual(['invoices.get', 'invoices.create'])
})
test('a kelte a budapesti idő szerinti nap', async () => {
const mock = createMockKassza({ now: () => new Date('2026-12-31T23:30:00Z') })
const szamlaszam = await szamlazFizetettRendelest(rendeles, mock)
expect(mock.invoiceRecords.get(szamlaszam)?.details.header.issueDate).toBe('2027-01-01')
})
```
Az időtúllépéses teszt csomagoló klienst használ, mert a `failNext()` a bizonylat létrehozása előtt dob. A csomagoló előbb kiállítja a számlát, és csak utána dob hibát, ahogy egy elveszett válasz után történne.
## Díjbekérő és számla [#díjbekérő-és-számla]
A [Nevezés díjbekérővel](/docs/receptek/nevezes-dijbekerovel) recept függvényei. Ezek a tesztek fogják meg azt a hibát, amikor a díjbekérőt kiállított számlának hinnéd.
```ts title="lib/nevezes-szamlazas.test.ts"
import { createMockKassza } from 'kassza/testing'
import { expect, test } from 'vitest'
import {
dijbekeroKiallitasa,
dijbekeroTorlese,
type Nevezes,
szamlaBefizetesUtan,
} from './nevezes-szamlazas'
const nevezes: Nevezes = {
id: '42',
vevo: {
name: 'Kiss Anna',
zip: '1134',
city: 'Budapest',
address: 'Váci út 10.',
email: 'anna@example.hu',
},
tav: 'félmaraton',
nevezesiDij: 12_990,
}
test('a befizetés után a díjbekérőre hivatkozó számla készül', async () => {
const mock = createMockKassza()
const dijbekeroszam = await dijbekeroKiallitasa(nevezes, mock)
const szamlaszam = await szamlaBefizetesUtan(nevezes, mock)
expect(szamlaszam).not.toBe(dijbekeroszam)
expect(mock.invoiceRecords.get(szamlaszam)?.input).toMatchObject({
proformaNumber: dijbekeroszam,
paid: true,
})
})
test('a befizetés kétszeri rögzítése sem állít ki két számlát', async () => {
const mock = createMockKassza()
await dijbekeroKiallitasa(nevezes, mock)
const elso = await szamlaBefizetesUtan(nevezes, mock)
const masodik = await szamlaBefizetesUtan(nevezes, mock)
expect(masodik).toBe(elso)
})
test('kifizetett nevezés díjbekérője nem törölhető', async () => {
const mock = createMockKassza()
await dijbekeroKiallitasa(nevezes, mock)
await szamlaBefizetesUtan(nevezes, mock)
await expect(dijbekeroTorlese(nevezes, mock)).resolves.toBe('mar-szamlazva')
})
test('a második törlés nem hiba', async () => {
const mock = createMockKassza()
await dijbekeroKiallitasa(nevezes, mock)
await expect(dijbekeroTorlese(nevezes, mock)).resolves.toBe('torolve')
await expect(dijbekeroTorlese(nevezes, mock)).resolves.toBe('nem-talalhato')
})
```
## Pénztári nyugta [#pénztári-nyugta]
A [Pénztári nyugta](/docs/receptek/penztari-nyugta) recept `nyugtaEladasrol()` függvénye. A mock ismételt `callId`-nál ugyanúgy 338-as `duplicate` hibát dob, mint a Számlázz.hu, a `createOnce()` pedig ilyenkor a rendelésszám alapján visszaadja a már elkészült nyugtát.
```ts title="lib/penztar.test.ts"
import { createMockKassza } from 'kassza/testing'
import { expect, test } from 'vitest'
import { type Eladas, nyugtaEladasrol } from './penztar'
const eladas: Eladas = {
id: '2026-0917-0042',
fizetesiMod: 'készpénz',
tetelek: [
{ name: 'Lángos', quantity: 2, grossUnitPrice: 1_490, vat: 5 },
{ name: 'Ásványvíz 0,5 l', grossUnitPrice: 590, vat: 27 },
],
}
test('ugyanarról az eladásról csak egy nyugta készül', async () => {
const mock = createMockKassza({ defaults: { receipt: { prefix: 'NYGT' } } })
const elso = await nyugtaEladasrol(eladas, mock)
const masodik = await nyugtaEladasrol(eladas, mock)
expect(masodik.number).toBe(elso.number)
expect(mock.receiptRecords.size).toBe(1)
expect(elso.totals.grossAmount).toBe(3_570)
})
```
## Route handler [#route-handler]
A route handlerek modulszinten importálják a klienst, ezért itt a `vi.mock()` cseréli le a `@/lib/kassza` modult mock kliensre, a saját adatbázis-függvényt pedig egy kémfüggvényre. A teszt az [IPN webhook](/docs/receptek/ipn-webhook) route handlerét hívja meg egy összerakott `Request`-tel.
```ts title="app/api/szamlazz/ipn/route.test.ts"
import type { MockKassza } from 'kassza/testing'
import { beforeEach, expect, test, vi } from 'vitest'
import { kassza } from '@/lib/kassza'
import { fizetesiAllapotMentese } from '@/lib/rendelesek'
import { POST } from './route'
vi.mock('@/lib/kassza', async () => {
const { createMockKassza } = await import('kassza/testing')
return { kassza: createMockKassza() }
})
vi.mock('@/lib/rendelesek', () => ({ fizetesiAllapotMentese: vi.fn() }))
const mock = kassza as MockKassza
function ipnKeres(mezok: Record, ip = '3.73.214.98'): Request {
return new Request('https://webshop.example.hu/api/szamlazz/ipn', {
method: 'POST',
headers: { 'content-type': 'application/x-www-form-urlencoded', 'x-forwarded-for': ip },
body: new URLSearchParams(mezok),
})
}
beforeEach(() => {
mock.reset()
vi.mocked(fizetesiAllapotMentese).mockClear()
})
test('a kifizetett számla állapotát menti', async () => {
const szamla = await mock.invoices.create({
orderNumber: 'WEB-5001',
paid: true,
buyer: { name: 'Nagy Péter', zip: '1111', city: 'Budapest', address: 'Fő utca 1.' },
items: [{ name: 'Póló', grossUnitPrice: 5_990, vat: 27 }],
})
const valasz = await POST(
ipnKeres({
szlahu_szamlaszam: szamla.number,
szlahu_bruttovegosszeg: String(szamla.grossTotal),
szlahu_kifizetettbrutto: String(szamla.grossTotal),
}),
)
expect(valasz.status).toBe(200)
expect(fizetesiAllapotMentese).toHaveBeenCalledWith(
expect.objectContaining({
szamlaszam: szamla.number,
rendelesszam: 'WEB-5001',
teljesenFizetve: true,
}),
)
})
test('idegen IP-címről érkező értesítést elutasít', async () => {
const valasz = await POST(ipnKeres({ szlahu_szamlaszam: 'WEB-2026-1' }, '203.0.113.7'))
expect(valasz.status).toBe(403)
expect(fizetesiAllapotMentese).not.toHaveBeenCalled()
})
```
## Buktatók [#buktatók]
A tesztkörnyezetben csak kamu kulcs vagy tesztfiók kulcs legyen. Ha egy teszt elfelejti átadni a
mockot, a függvény a valódi klienssel hívná a Számlázz.hu-t. Kamu kulccsal ez hitelesítési hibára
fut, és bizonylat nem készül, de éles kulccsal valódi számlát állítana ki.
A mock nem ellenőrzi a rendelésszám ismétlődését (71, 152), a nem regisztrált előtagot (202) és
a fiók állapotát. Ezeket a `failNext()` második paraméterében megadott `SzamlazzError`-ral
szimuláld, a teljes folyamatot pedig élesítés előtt tesztfiókkal is próbáld ki.
A mock számlaszámai `E-KASSZA-2026-1` alakúak, a díjbekérőké `D-KASSZA-2026-1`. Ne építs
tesztet a Számlázz.hu valódi sorszámformátumára, inkább a visszakapott számot hasonlítsd össze.
Az `invoices.find()` a hívásnaplóban `invoices.get` néven szerepel, ezért a validációs teszt
`['invoices.get', 'invoices.create']` sorrendet vár.
## Kapcsolódó [#kapcsolódó]
A mock kliens összes opciója, mezője és szimulált viselkedése.
A tesztelt számlázó függvény.
A tesztelt díjbekérős folyamat.
Mely hibák után kell lekérdezni, és melyek után nem.