kassza 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +150 -0
- package/agents/README.md +59 -0
- package/agents/api.md +206 -0
- package/agents/pitfalls.md +52 -0
- package/agents/recipes.md +217 -0
- package/agents/skills/kassza/SKILL.md +29 -0
- package/assets/logo-wordmark.svg +25 -0
- package/assets/logo.svg +19 -0
- package/dist/binary-B89HM03_.cjs +63 -0
- package/dist/binary-D0M9U2MD.js +34 -0
- package/dist/client-B9kbKKBf.d.cts +748 -0
- package/dist/client-zv4enyq7.d.ts +748 -0
- package/dist/cookie-stores/index.cjs +153 -0
- package/dist/cookie-stores/index.d.cts +63 -0
- package/dist/cookie-stores/index.d.ts +63 -0
- package/dist/cookie-stores/index.js +144 -0
- package/dist/create-BZd6CUO7.cjs +1160 -0
- package/dist/create-DDZaNlOz.js +939 -0
- package/dist/dates-D2XebOIg.js +34 -0
- package/dist/dates-DtHzwNU6.d.cts +7 -0
- package/dist/dates-DtHzwNU6.d.ts +7 -0
- package/dist/dates-PPxH0n5H.cjs +57 -0
- package/dist/errors-B0QJhUW1.cjs +302 -0
- package/dist/errors-DapdV5DK.js +273 -0
- package/dist/index-CYAGCdKJ.d.cts +56 -0
- package/dist/index-CYAGCdKJ.d.ts +56 -0
- package/dist/index.cjs +1406 -0
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +1394 -0
- package/dist/ipn/index.cjs +108 -0
- package/dist/ipn/index.d.cts +32 -0
- package/dist/ipn/index.d.ts +32 -0
- package/dist/ipn/index.js +101 -0
- package/dist/money/index.cjs +15 -0
- package/dist/money/index.d.cts +2 -0
- package/dist/money/index.d.ts +2 -0
- package/dist/money/index.js +2 -0
- package/dist/money-C9j7nBNP.js +258 -0
- package/dist/money-DkiGukAw.cjs +335 -0
- package/dist/session-CRGOuz-R.cjs +100 -0
- package/dist/session-Dm_45x8K.d.cts +11 -0
- package/dist/session-Dm_45x8K.d.ts +11 -0
- package/dist/session-OJMLGufT.js +77 -0
- package/dist/shared-CrxlxI8n.cjs +207 -0
- package/dist/shared-D6DLa4b7.js +100 -0
- package/dist/storage/fs.cjs +88 -0
- package/dist/storage/fs.d.cts +13 -0
- package/dist/storage/fs.d.ts +13 -0
- package/dist/storage/fs.js +87 -0
- package/dist/storage/index.cjs +643 -0
- package/dist/storage/index.d.cts +288 -0
- package/dist/storage/index.d.ts +288 -0
- package/dist/storage/index.js +619 -0
- package/dist/testing/index.cjs +409 -0
- package/dist/testing/index.d.cts +35 -0
- package/dist/testing/index.d.ts +35 -0
- package/dist/testing/index.js +407 -0
- package/dist/types-DVRgwcqg.d.cts +26 -0
- package/dist/types-DVRgwcqg.d.ts +26 -0
- package/dist/validators/index.cjs +296 -0
- package/dist/validators/index.d.cts +51 -0
- package/dist/validators/index.d.ts +51 -0
- package/dist/validators/index.js +278 -0
- package/llms.txt +16 -0
- package/package.json +151 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 futozs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://cdn.jsdelivr.net/npm/kassza@0/assets/logo-wordmark.svg" alt="kassza" width="480">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<b>Számlázz.hu, TypeScriptben, egyszerűbben.</b><br>
|
|
7
|
+
Számla, díjbekérő, nyugta, sztornó, befizetés, PDF, adószám. Mind a 11 Agent művelet, 0 függőség.
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
<p align="center">
|
|
11
|
+
<a href="https://www.npmjs.com/package/kassza"><img src="https://img.shields.io/npm/v/kassza?color=14532D" alt="npm"></a>
|
|
12
|
+
<a href="https://www.npmjs.com/package/kassza"><img src="https://img.shields.io/npm/dm/kassza?color=14532D" alt="letöltések"></a>
|
|
13
|
+
<img src="https://img.shields.io/badge/függőség-0-14532D" alt="0 függőség">
|
|
14
|
+
<img src="https://img.shields.io/npm/l/kassza?color=14532D" alt="MIT">
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm i kassza
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Node 22+, Bun, Deno, Cloudflare Workers és Vercel Edge alatt is fut.
|
|
22
|
+
|
|
23
|
+
## Számla 10 sorban
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { createKassza } from 'kassza'
|
|
27
|
+
|
|
28
|
+
const kassza = createKassza({ agentKey: process.env.SZAMLAZZ_AGENT_KEY })
|
|
29
|
+
|
|
30
|
+
const szamla = await kassza.invoices.create({
|
|
31
|
+
buyer: { name: 'Vevő Kft.', zip: '1111', city: 'Budapest', address: 'Fő utca 1.', email: 'vevo@example.hu' },
|
|
32
|
+
items: [{ name: 'Webfejlesztés', quantity: 10, unit: 'óra', netUnitPrice: 15_000, vat: 27 }],
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
szamla.number
|
|
36
|
+
szamla.grossTotal
|
|
37
|
+
szamla.pdf
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Ha van e-mail cím, a Számlázz.hu ki is küldi a számlát. A kerekítést, a magyar dátumot és az XML-t a csomag intézi.
|
|
41
|
+
|
|
42
|
+
## Nyugta
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
const nyugta = await kassza.receipts.create({
|
|
46
|
+
prefix: 'NYGT',
|
|
47
|
+
paymentMethod: 'bankkártya',
|
|
48
|
+
items: [{ name: 'Kávé', grossUnitPrice: 890, vat: 27 }],
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
await kassza.receipts.send({ receiptNumber: nyugta.number, emails: 'vevo@example.hu' })
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Díjbekérő, befizetés, számla
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
const dijbekero = await kassza.invoices.create({
|
|
58
|
+
type: 'proforma',
|
|
59
|
+
orderNumber: 'REND-42',
|
|
60
|
+
buyer,
|
|
61
|
+
items: [{ name: 'Nevezési díj', grossUnitPrice: 26_000, vat: 27 }],
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const szamla = await kassza.invoices.create({ proformaNumber: dijbekero.number, orderNumber: 'REND-42', buyer, items })
|
|
65
|
+
await kassza.invoices.registerPayment({ invoiceNumber: szamla.number, amount: szamla.grossTotal })
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Minden más
|
|
69
|
+
|
|
70
|
+
| Mit | Hogyan |
|
|
71
|
+
|---|---|
|
|
72
|
+
| Előleg-, vég-, helyesbítő számla, szállítólevél | `type: 'advance' \| 'final' \| 'corrective' \| 'deliveryNote'` |
|
|
73
|
+
| Bruttó ár (B2C) vagy nettó ár (B2B) | `grossUnitPrice` vagy `netUnitPrice` |
|
|
74
|
+
| Devizás számla | `currency: 'EUR'` (az árfolyam alapból MNB) |
|
|
75
|
+
| Előnézeti PDF, bizonylat nélkül | `kassza.invoices.preview(input)` |
|
|
76
|
+
| Sztornó | `kassza.invoices.reverse('E-2026-12')` |
|
|
77
|
+
| PDF utólag | `kassza.invoices.getPdf({ orderNumber: 'REND-42' })` |
|
|
78
|
+
| Teljes számla adatai | `kassza.invoices.get(...)` vagy `find(...)` (`null`, ha nincs) |
|
|
79
|
+
| Díjbekérő törlése | `kassza.invoices.deleteProforma({ orderNumber: 'REND-42' })` |
|
|
80
|
+
| Nyugta sztornó, lekérdezés | `kassza.receipts.reverse(...)`, `get(...)`, `find(...)` |
|
|
81
|
+
| Cégadatok adószámból (NAV) | `kassza.taxpayer.query('13421739')` |
|
|
82
|
+
| Jó-e az Agent kulcs? | `kassza.verifyCredentials()` |
|
|
83
|
+
|
|
84
|
+
Közös alapértékek egyszer, a kliensnél:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
const kassza = createKassza({
|
|
88
|
+
defaults: {
|
|
89
|
+
invoice: { prefix: 'WEB', paymentDueInDays: 8, seller: { emailReplyTo: 'penzugy@ceg.hu' } },
|
|
90
|
+
receipt: { prefix: 'NYGT', paymentMethod: 'bankkártya' },
|
|
91
|
+
},
|
|
92
|
+
})
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Az `agentKey` elhagyható, ha a `SZAMLAZZ_AGENT_KEY` környezeti változó be van állítva.
|
|
96
|
+
|
|
97
|
+
## Hibák
|
|
98
|
+
|
|
99
|
+
Minden hiba `SzamlazzError`, magyar üzenettel és tippel.
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import { isSzamlazzError } from 'kassza'
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
await kassza.invoices.create(input)
|
|
106
|
+
} catch (error) {
|
|
107
|
+
if (!isSzamlazzError(error)) throw error
|
|
108
|
+
error.code
|
|
109
|
+
error.category
|
|
110
|
+
error.hint
|
|
111
|
+
error.isDuplicate
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
A kategóriák: `auth`, `account`, `validation`, `duplicate`, `not_found`, `partial_success`, `maintenance`, `network`, `timeout`, `configuration`, `unexpected_response`.
|
|
116
|
+
|
|
117
|
+
A csomag magától csak ott próbálkozik újra, ahol ez biztonságos: lekérdezésnél, hálózati hibánál és karbantartásnál, legfeljebb 5-ször. Számlát soha nem küld újra. A Számlázz.hu kitiltja azt, aki ciklusban próbálkozik.
|
|
118
|
+
|
|
119
|
+
## Tesztelés API hívás nélkül
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
import { createMockKassza } from 'kassza/testing'
|
|
123
|
+
|
|
124
|
+
const kassza = createMockKassza()
|
|
125
|
+
const szamla = await kassza.invoices.create(input)
|
|
126
|
+
kassza.calls
|
|
127
|
+
kassza.failNext('invoices.create')
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
A mock ugyanazt a validációt és kerekítést futtatja, mint az éles kliens.
|
|
131
|
+
|
|
132
|
+
## Kiegészítők
|
|
133
|
+
|
|
134
|
+
| Import | Mire jó |
|
|
135
|
+
|---|---|
|
|
136
|
+
| `kassza/ipn` | Fizetési értesítés (IPN) webhook: `readIpnNotification(request)` |
|
|
137
|
+
| `kassza/validators` | Adószám, bankszámla, IBAN, irányítószám, cím, EU adószám ellenőrzés |
|
|
138
|
+
| `kassza/money` | Nettó/bruttó/áfa számítás a hivatalos kerekítési szabályokkal |
|
|
139
|
+
| `kassza/storage` | PDF mentése S3, R2, Vercel Blob, UploadThing, Supabase tárhelyre |
|
|
140
|
+
| `kassza/storage/fs` | PDF mentése fájlrendszerbe (Node) |
|
|
141
|
+
| `kassza/cookie-stores` | Session megosztás serverlessben: Upstash, Redis, Cloudflare KV |
|
|
142
|
+
| `kassza/testing` | Mock kliens unit tesztekhez |
|
|
143
|
+
|
|
144
|
+
## AI-val kódolsz?
|
|
145
|
+
|
|
146
|
+
A csomagban van egy `agents/` mappa, amiből a Claude Code, a Cursor, a Copilot és a többi asszisztens megtudja, hogyan kell jól használni a kasszát, és mik a buktatók. Mutasd meg neki: `node_modules/kassza/agents/README.md`.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
Nem hivatalos csomag, nem kapcsolódik a KBOSS.hu Kft.-hez (Számlázz.hu). Hivatalos dokumentáció: [docs.szamlazz.hu](https://docs.szamlazz.hu/hu/agent/). MIT licenc.
|
package/agents/README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# kassza for AI coding agents
|
|
2
|
+
|
|
3
|
+
`kassza` is a zero-dependency TypeScript client for the Számlázz.hu Számla Agent API, the most widely used Hungarian invoicing service. It covers all 11 Agent operations:
|
|
4
|
+
|
|
5
|
+
- Invoices: create (invoice, proforma, advance, final, corrective, delivery note), preview, reverse, register payment, PDF, full invoice data, delete proforma.
|
|
6
|
+
- Receipts (nyugta): create, reverse, get, send by email.
|
|
7
|
+
- Taxpayer lookup from the NAV database.
|
|
8
|
+
|
|
9
|
+
It runs on Node 22+, Bun, Deno, Cloudflare Workers and Vercel Edge.
|
|
10
|
+
|
|
11
|
+
## Read in this order
|
|
12
|
+
|
|
13
|
+
1. [pitfalls.md](./pitfalls.md): hard rules. Follow them or you will create duplicate or invalid tax documents.
|
|
14
|
+
2. [api.md](./api.md): every method, input and output.
|
|
15
|
+
3. [recipes.md](./recipes.md): webhooks, idempotent invoicing, receipts, IPN, PDF storage, serverless, tests.
|
|
16
|
+
|
|
17
|
+
A ready-made Claude Code skill is in [skills/kassza/SKILL.md](./skills/kassza/SKILL.md). Copy the `skills/kassza` folder into `.claude/skills/` in the user's project.
|
|
18
|
+
|
|
19
|
+
## The five things to get right
|
|
20
|
+
|
|
21
|
+
1. Create the client once, on the server only: `createKassza()`. It reads `SZAMLAZZ_AGENT_KEY`.
|
|
22
|
+
2. Always set `orderNumber` on invoices and `callId` on receipts, derived from the order ID.
|
|
23
|
+
3. Give prices as `netUnitPrice` or `grossUnitPrice` plus `vat`, and never compute net, VAT or gross yourself.
|
|
24
|
+
4. Never retry `create` in a loop. After a `network`, `timeout`, `partial_success` or `duplicate` error, call `invoices.find({ orderNumber })`.
|
|
25
|
+
5. In tests, use `createMockKassza()` from `kassza/testing` instead of the real API.
|
|
26
|
+
|
|
27
|
+
## Minimal example
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { createKassza } from 'kassza'
|
|
31
|
+
|
|
32
|
+
const kassza = createKassza()
|
|
33
|
+
|
|
34
|
+
const invoice = await kassza.invoices.create({
|
|
35
|
+
orderNumber: 'ORDER-1001',
|
|
36
|
+
buyer: { name: 'Vevő Kft.', zip: '1111', city: 'Budapest', address: 'Fő utca 1.', email: 'vevo@example.hu' },
|
|
37
|
+
items: [{ name: 'Termék', quantity: 2, grossUnitPrice: 12_700, vat: 27 }],
|
|
38
|
+
})
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Vocabulary
|
|
42
|
+
|
|
43
|
+
| Hungarian | kassza |
|
|
44
|
+
|---|---|
|
|
45
|
+
| számla | invoice |
|
|
46
|
+
| díjbekérő | proforma |
|
|
47
|
+
| előlegszámla / végszámla | advance / final |
|
|
48
|
+
| helyesbítő számla | corrective |
|
|
49
|
+
| szállítólevél | deliveryNote |
|
|
50
|
+
| sztornó | reverse / reversal |
|
|
51
|
+
| nyugta | receipt |
|
|
52
|
+
| befizetés, jóváírás | payment |
|
|
53
|
+
| rendelésszám | orderNumber |
|
|
54
|
+
| hívásazonosító | callId |
|
|
55
|
+
| számlaszám előtag | prefix |
|
|
56
|
+
| adószám / közösségi adószám | taxNumber / euTaxNumber |
|
|
57
|
+
| áfakulcs | vat |
|
|
58
|
+
| nettó / bruttó egységár | netUnitPrice / grossUnitPrice |
|
|
59
|
+
| Agent kulcs | agentKey |
|
package/agents/api.md
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# kassza: API reference for coding agents
|
|
2
|
+
|
|
3
|
+
All types are exported from `kassza`, so import them instead of redefining them. Every method throws `SzamlazzError` on failure and accepts an optional last argument `{ signal?: AbortSignal }`.
|
|
4
|
+
|
|
5
|
+
## Client
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { createKassza } from 'kassza'
|
|
9
|
+
|
|
10
|
+
const kassza = createKassza({
|
|
11
|
+
agentKey?: string,
|
|
12
|
+
username?: string, password?: string,
|
|
13
|
+
defaults?: { invoice?: InvoiceDefaults, receipt?: ReceiptDefaults },
|
|
14
|
+
cookieStore?: CookieStore | false,
|
|
15
|
+
timeoutMs?: number,
|
|
16
|
+
maxAttempts?: number,
|
|
17
|
+
retryDelayMs?: number,
|
|
18
|
+
fetch?: typeof fetch,
|
|
19
|
+
endpoint?: string,
|
|
20
|
+
hooks?: { onRequest?, onResponse?, onError? },
|
|
21
|
+
})
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
- `agentKey` falls back to the `SZAMLAZZ_AGENT_KEY` environment variable.
|
|
25
|
+
- `username` and `password` are legacy credentials; prefer the Agent key.
|
|
26
|
+
- `cookieStore` defaults to an in-memory store; `false` disables session reuse.
|
|
27
|
+
- `timeoutMs` defaults to 60000.
|
|
28
|
+
- `maxAttempts` defaults to 3 and is capped at 5. It applies only to operations that are safe to retry.
|
|
29
|
+
- `hooks` receive events for logging. They never receive the XML payload or the key.
|
|
30
|
+
|
|
31
|
+
## Invoices: `kassza.invoices`
|
|
32
|
+
|
|
33
|
+
| Method | Returns | Retried automatically |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| `create(input: CreateInvoiceInput)` | `CreatedInvoice` | never |
|
|
36
|
+
| `preview(input: CreateInvoiceInput)` | `InvoicePreview` (PDF only, no document is created) | never |
|
|
37
|
+
| `reverse(input: string \| ReverseInvoiceOptions)` | `ReversedInvoice` | never |
|
|
38
|
+
| `registerPayment(input: RegisterPaymentInput)` | `RegisteredPayment` | only when `additive: false` |
|
|
39
|
+
| `clearPayments(input: string \| { invoiceNumber })` | `RegisteredPayment` | yes |
|
|
40
|
+
| `getPdf(ref: InvoiceReference)` | `InvoicePdf` | yes |
|
|
41
|
+
| `get(ref: InvoiceReference, { includePdf? })` | `InvoiceDetails` | yes |
|
|
42
|
+
| `find(ref: InvoiceReference, { includePdf? })` | `InvoiceDetails \| null` | yes |
|
|
43
|
+
| `deleteProforma(ref: string \| { proformaNumber } \| { orderNumber })` | `void` | never |
|
|
44
|
+
|
|
45
|
+
`InvoiceReference` is `string` (the invoice number), `{ invoiceNumber }`, `{ orderNumber }`, or `{ externalId }`. When several documents share an order number, the latest one is returned.
|
|
46
|
+
|
|
47
|
+
### CreateInvoiceInput
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
{
|
|
51
|
+
type?: 'invoice' | 'proforma' | 'advance' | 'deliveryNote'
|
|
52
|
+
| 'final' | 'corrective',
|
|
53
|
+
advanceInvoiceNumber?: string,
|
|
54
|
+
correctedInvoiceNumber?: string,
|
|
55
|
+
|
|
56
|
+
buyer: {
|
|
57
|
+
name: string, zip: string, city: string, address: string,
|
|
58
|
+
country?, email?, sendEmail?, taxNumber?, euTaxNumber?, groupTaxNumber?,
|
|
59
|
+
taxpayerType?: 'hungarianTaxNumber' | 'euBusiness' | 'nonEuBusiness' | 'noTaxNumber' | 'unknown',
|
|
60
|
+
postal?: { name?, country?, zip?, city?, address? },
|
|
61
|
+
identifier?, phone?, comment?, signatoryName?, ledger?,
|
|
62
|
+
},
|
|
63
|
+
items: Array<{
|
|
64
|
+
name: string,
|
|
65
|
+
vat: VatRate,
|
|
66
|
+
quantity?: number,
|
|
67
|
+
unit?: string,
|
|
68
|
+
netUnitPrice?: number,
|
|
69
|
+
grossUnitPrice?: number,
|
|
70
|
+
netAmount?, vatAmount?, grossAmount?,
|
|
71
|
+
identifier?, comment?, ledger?, dataDeletionCode?, marginVatBase?,
|
|
72
|
+
}>,
|
|
73
|
+
|
|
74
|
+
issueDate?: Date | 'YYYY-MM-DD',
|
|
75
|
+
fulfillmentDate?, dueDate?, paymentDueInDays?: number,
|
|
76
|
+
paymentMethod?: string,
|
|
77
|
+
currency?: string,
|
|
78
|
+
exchangeRate?: number,
|
|
79
|
+
exchangeBank?: string,
|
|
80
|
+
language?: 'hu' | 'en' | 'de' | 'it' | 'ro' | 'sk' | 'hr' | 'fr' | 'es' | 'cz' | 'pl' | 'bg' | 'nl' | 'ru' | 'si',
|
|
81
|
+
orderNumber?: string,
|
|
82
|
+
proformaNumber?: string,
|
|
83
|
+
externalId?: string,
|
|
84
|
+
prefix?: string,
|
|
85
|
+
comment?, paid?: boolean,
|
|
86
|
+
eInvoice?: boolean,
|
|
87
|
+
downloadPdf?: boolean,
|
|
88
|
+
seller?: { bank?, bankAccount?, emailReplyTo?, emailSubject?, emailText?, signatoryName? },
|
|
89
|
+
template?: 'SzlaMost' | 'SzlaAlap' | 'SzlaNoEnv' | 'Szla8cm' | 'SzlaTomb' | 'SzlaFuvarlevelesAlap',
|
|
90
|
+
attachments?: Array<{ filename, content: Uint8Array | ArrayBuffer | Blob | string, contentType? }>,
|
|
91
|
+
waybill?, simpleItems?, euVat?, marginVat?, paymentCorrection?, logoExtra?,
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Notes on the fields:
|
|
96
|
+
|
|
97
|
+
- `type` defaults to `'invoice'`. `'final'` optionally takes `advanceInvoiceNumber`, and `'corrective'` requires `correctedInvoiceNumber`.
|
|
98
|
+
- In `items`:
|
|
99
|
+
- `quantity` defaults to 1 and may be negative on final or corrective invoices.
|
|
100
|
+
- `unit` defaults to `'db'`.
|
|
101
|
+
- Give exactly one of `netUnitPrice` (B2B, net-based rounding) and `grossUnitPrice` (B2C, gross-based rounding).
|
|
102
|
+
- Use `netAmount`, `vatAmount` and `grossAmount` only to override the calculation, and give all three.
|
|
103
|
+
- All dates default to today in `Europe/Budapest`.
|
|
104
|
+
- `paymentMethod` defaults to `'Átutalás'`.
|
|
105
|
+
- `currency` defaults to `'HUF'`. For other currencies, `exchangeBank` defaults to MNB, and `exchangeRate` is required unless the bank is MNB.
|
|
106
|
+
- `orderNumber` is your ID. Always set it, because it enables `find` and deduplication.
|
|
107
|
+
- `proformaNumber` links the invoice to the proforma it was paid from. `externalId` is an ID for lookups.
|
|
108
|
+
- `prefix` must be registered in the Számlázz.hu account (error 202).
|
|
109
|
+
- `eInvoice` defaults to `false`, and `downloadPdf` defaults to `true`.
|
|
110
|
+
- `attachments` allows at most 5 files, 2 MB each, and they are sent only by email.
|
|
111
|
+
|
|
112
|
+
`VatRate` is one of 0, 5, 18, 27 (plus the other rates allowed by Számlázz.hu) or one of `'TAM' | 'AAM' | 'EUT' | 'EUKT' | 'F.AFA' | 'K.AFA' | 'TAHK' | 'HO' | 'EUE' | 'EUFADE' | 'EUFAD37' | 'ATK' | 'NAM' | 'EAM' | 'KBAUK' | 'KBAET'`.
|
|
113
|
+
|
|
114
|
+
`CreatedInvoice` is `{ number, netTotal, grossTotal, outstanding?, buyerAccountUrl?, pdf?: Uint8Array, items: ItemAmounts[] }`.
|
|
115
|
+
|
|
116
|
+
### RegisterPaymentInput
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
{ invoiceNumber, amount, method?: string, date?, description?, additive? }
|
|
120
|
+
{ invoiceNumber, payments: Array<{ amount, method, date?, description? }>, additive?, taxNumber? }
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
- The single-payment form defaults `method` to `'átutalás'` and `date` to today.
|
|
124
|
+
- The multi-payment form accepts at most 5 payments.
|
|
125
|
+
- `additive` defaults to `true`, which appends to earlier payments; `false` replaces them.
|
|
126
|
+
|
|
127
|
+
### ReverseInvoiceOptions
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
{ invoiceNumber, issueDate?, fulfillmentDate?, comment?, template?, eInvoice?, downloadPdf?, externalId?,
|
|
131
|
+
email?: { replyTo?, subject?, text? }, buyer?: { email?, taxNumber?, euTaxNumber? } }
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Receipts: `kassza.receipts`
|
|
135
|
+
|
|
136
|
+
| Method | Returns | Retried automatically |
|
|
137
|
+
|---|---|---|
|
|
138
|
+
| `create(input: CreateReceiptInput)` | `Receipt` | only with `callId` |
|
|
139
|
+
| `reverse(input: string \| { receiptNumber, callId?, downloadPdf?, template? })` | `Receipt` | only with `callId` |
|
|
140
|
+
| `get(input: string \| { receiptNumber } \| { orderNumber }, + downloadPdf?)` | `Receipt` | yes |
|
|
141
|
+
| `find(same as get)` | `Receipt \| null` | yes |
|
|
142
|
+
| `send({ receiptNumber, emails: string \| string[], replyTo?, subject?, text? })` | `void` | never |
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
{
|
|
146
|
+
prefix: string,
|
|
147
|
+
paymentMethod: string,
|
|
148
|
+
items: Array<{ name, vat: VatRate | 'ÁKK' | 'MAA' | 'EU' | 'EUK', quantity?, unit?,
|
|
149
|
+
netUnitPrice? | grossUnitPrice?, identifier?, comment?, ledger? }>,
|
|
150
|
+
callId?: string,
|
|
151
|
+
orderNumber?, comment?, currency?, exchangeRate?, exchangeBank?,
|
|
152
|
+
payments?: Array<{ method, amount, description? }>,
|
|
153
|
+
template?: 'A' | 'N' | 'J' | 'L', downloadPdf?,
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
- `prefix` accepts uppercase letters and digits only. Give it in the input or in `defaults.receipt`.
|
|
158
|
+
- `paymentMethod` has no default: `'készpénz'`, `'bankkártya'` or anything else must be given here or in `defaults.receipt`.
|
|
159
|
+
- `callId` is an idempotency key; set it from your order ID.
|
|
160
|
+
- If you pass `payments`, their sum must equal the gross total.
|
|
161
|
+
|
|
162
|
+
`Receipt` is `{ id, number, callId?, type: 'receipt' | 'reversal', isReversed, reversedReceiptNumber?, issueDate, paymentMethod, currency, orderNumber?, items, payments, totals: { netAmount, vatAmount, grossAmount, byVat }, pdf? }`.
|
|
163
|
+
|
|
164
|
+
## Taxpayer: `kassza.taxpayer`
|
|
165
|
+
|
|
166
|
+
`query(taxNumber: string): TaxpayerInfo` accepts `12345678`, `12345678-2-42` or `HU12345678`.
|
|
167
|
+
|
|
168
|
+
`TaxpayerInfo` is `{ valid, name?, shortName?, taxNumber?: { taxpayerId, vatCode?, countyCode?, formatted? }, address?: TaxpayerAddress, addresses, incorporation?, infoDate? }`, and `TaxpayerAddress.formatted` looks like `'1031 Budapest, Záhony utca 7.'`.
|
|
169
|
+
|
|
170
|
+
## Other
|
|
171
|
+
|
|
172
|
+
- `kassza.verifyCredentials(): Promise<boolean>` returns `false` for a wrong key and throws for account problems.
|
|
173
|
+
- `kassza.resetSession()` clears the session. Call it after changing account data in Számlázz.hu.
|
|
174
|
+
|
|
175
|
+
## Subpath modules
|
|
176
|
+
|
|
177
|
+
### `kassza/testing`
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
createMockKassza({ defaults?, taxpayers?: Record<taxpayerId, TaxpayerInfo>, credentialsValid?, now?: () => Date }): MockKassza
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
- A `MockKassza` has the same interface as `Kassza`, plus `calls`, `invoiceRecords`, `receiptRecords`, `failNext(method, error?)` and `reset()`.
|
|
184
|
+
- The method names used in `calls` and `failNext` look like `'invoices.create'` and `'receipts.send'`.
|
|
185
|
+
- The mock runs the real validation and rounding, and throws the same error codes: 7, 335, 338 and 339.
|
|
186
|
+
|
|
187
|
+
### `kassza/ipn`
|
|
188
|
+
|
|
189
|
+
- `readIpnNotification(request: Request): Promise<IpnNotification>`, for Next.js route handlers, Hono, Workers and similar.
|
|
190
|
+
- `parseIpnNotification(body: string | URLSearchParams | FormData | Record<string, string>): IpnNotification`
|
|
191
|
+
- `IpnNotification` is `{ invoiceNumber, proformaNumber?, orderNumber?, grossTotal, paidAmount, paymentMethod?, paymentDate?, isFullyPaid, raw }`.
|
|
192
|
+
- `ipnOkResponse(): Response`
|
|
193
|
+
- `isSzamlazzIp(ip)` and `SZAMLAZZ_OUTBOUND_IPS`.
|
|
194
|
+
|
|
195
|
+
### `kassza/validators`
|
|
196
|
+
|
|
197
|
+
- Tax number: `parseHungarianTaxNumber`, `isValidHungarianTaxNumber` (CDV check), `isValidHungarianTaxpayerId`, `isValidHungarianGroupTaxNumber`.
|
|
198
|
+
- Bank account: `parseHungarianBankAccount`, `isValidHungarianBankAccount`, `formatHungarianBankAccount`, `isValidHungarianIban`.
|
|
199
|
+
- Address: `isValidHungarianZipCode`, `parseHungarianAddress('1234 Budapest, Fő utca 1.')` returns `{ zip, city, address, district? }`.
|
|
200
|
+
- Other: `isValidEuVatNumber`, `normalizeEuVatNumber`, `isValidEmail`, `normalizeEmail`, `isValidAgentKey`.
|
|
201
|
+
|
|
202
|
+
### `kassza/money`
|
|
203
|
+
|
|
204
|
+
- `calculateInvoiceItem(input, currency?)` and `calculateReceiptItem(input, currency?)` return `ItemAmounts`.
|
|
205
|
+
- `summarizeItems(items)` returns `{ netAmount, vatAmount, grossAmount, byVat }`.
|
|
206
|
+
- `roundMoney(value, decimals)`, `isVatRate(value)`, `NUMERIC_VAT_RATES`, `SPECIAL_VAT_CODES`.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# kassza: rules and pitfalls
|
|
2
|
+
|
|
3
|
+
Read this before writing any code that issues invoices or receipts. These rules come from the official Számlázz.hu docs and from production incidents. Breaking them produces invalid tax documents, duplicate invoices, or a banned API key.
|
|
4
|
+
|
|
5
|
+
## Hard rules
|
|
6
|
+
|
|
7
|
+
1. **Never retry creating an invoice or receipt in a loop.** Számlázz.hu bans accounts that resend requests. kassza already retries only when it is safe to do so. Do not wrap `invoices.create` in your own retry loop.
|
|
8
|
+
2. **After an uncertain failure, look the document up before creating it again.** A timeout, a network error, or error `56` (`partial_success`) can mean the invoice was created anyway. Always set an `orderNumber`, and on those errors call `kassza.invoices.find({ orderNumber })` first.
|
|
9
|
+
3. **Do not compute item amounts yourself.** Pass `netUnitPrice` (B2B) or `grossUnitPrice` (B2C) plus `vat`, and kassza applies the official rounding rules. Hand-computed floats cause errors 259–264, and on receipts 261 and 363–365.
|
|
10
|
+
4. **Never use `new Date().toISOString().slice(0, 10)` for invoice dates.** Between midnight and 02:00 Budapest time it returns yesterday, which gives error 352. Leave dates out (kassza defaults to today in `Europe/Budapest`) or pass a `Date`.
|
|
11
|
+
5. **The Agent key is a secret and must be lowercase.** Read it from `SZAMLAZZ_AGENT_KEY` on the server only, and never ship it to a browser bundle. kassza rejects keys containing uppercase letters before sending anything.
|
|
12
|
+
6. **Use the test account while developing.** It allows at most 500 invoices per 10 minutes. Never run tests against a production Agent key.
|
|
13
|
+
|
|
14
|
+
## Behaviour worth knowing
|
|
15
|
+
|
|
16
|
+
| Situation | What happens | What to do |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| Buyer has an `email` | `sendEmail` defaults to `true`, so Számlázz.hu emails the invoice | Set `buyer.sendEmail: false` to suppress it |
|
|
19
|
+
| Error `71` / `152` (`duplicate`) | The account forbids repeating an order number; the document probably exists already | `find({ orderNumber })` and reuse it |
|
|
20
|
+
| Error `56` (`partial_success`) | The invoice was created, but the notification email failed | Do **not** create it again |
|
|
21
|
+
| Error `7` | Lookup found nothing (or a required field is missing) | `find()` returns `null` for this |
|
|
22
|
+
| Error `202` | The invoice prefix is not registered in the account | Add it under Beállítások / Előtagok, or remove `prefix` |
|
|
23
|
+
| Error `136` | The subscription has expired or is unpaid | A human has to log in to szamlazz.hu |
|
|
24
|
+
| Error `54` | E-invoicing is not enabled in the account | Use `eInvoice: false` (the default) |
|
|
25
|
+
| Receipt prefix | Uppercase letters and digits only, and it must not be a prefix already used on invoices (336, 337) | Use a dedicated prefix such as `NYGT` |
|
|
26
|
+
| Receipt `callId` | Idempotency key; resending the same `callId` returns error 338 instead of a duplicate | Always set it from your order ID |
|
|
27
|
+
| Final invoice (`type: 'final'`) | Needs `advanceInvoiceNumber` or the same `orderNumber` as the advance invoice | Only one final invoice per advance invoice |
|
|
28
|
+
| `invoices.get` / XML query | Works only for outgoing invoices issued in Számlázz.hu | Use `getPdf` for the PDF only |
|
|
29
|
+
| Reversing a proforma or delivery note | Számlázz.hu returns the original document and reverses nothing; kassza throws a `validation` error | Use `invoices.deleteProforma` instead |
|
|
30
|
+
| `registerPayment` | `additive` defaults to `true` and appends to earlier payments | Use `clearPayments` to wipe them |
|
|
31
|
+
| Session cookie | Expires after 90 minutes of inactivity; the in-memory store is per process | In serverless, pass a shared `cookieStore` from `kassza/cookie-stores` |
|
|
32
|
+
| IPN webhook | Retried every 3 minutes, at most 10 times; only the latest one per invoice is sent | Respond with HTTP 200 quickly and process idempotently |
|
|
33
|
+
| IPN source check | `isSzamlazzIp` trusts the first `x-forwarded-for` entry | Use it only behind a proxy you control |
|
|
34
|
+
| `buyer.groupTaxNumber`, item `dataDeletionCode` | Documented on docs.szamlazz.hu, but missing from the downloadable `xmlszamla.xsd` (and `torloKod` from `xmlnyugtacreate.xsd`) | Use them only when needed; if you get error 57, remove them |
|
|
35
|
+
| NAV receipt data reporting | Mandatory since 2026-09-01, with a grace period until 2026-12-31 | No code change needed yet; watch the kassza changelog |
|
|
36
|
+
|
|
37
|
+
## Error categories
|
|
38
|
+
|
|
39
|
+
`SzamlazzError.category` is one of:
|
|
40
|
+
|
|
41
|
+
| Category | Meaning | Retry? |
|
|
42
|
+
|---|---|---|
|
|
43
|
+
| `auth` | Wrong key, or a browser session is interfering | No, fix the credentials |
|
|
44
|
+
| `account` | Subscription, e-invoice, or data-deletion-code settings | No, a human must act |
|
|
45
|
+
| `validation` | The request is wrong (client-side or server-side check) | No, fix the input |
|
|
46
|
+
| `duplicate` | Order number or `callId` already used | No, look the document up |
|
|
47
|
+
| `not_found` | The document does not exist | No |
|
|
48
|
+
| `partial_success` | The document exists, but a side effect (email) failed | No |
|
|
49
|
+
| `maintenance` | Számlázz.hu maintenance (code 1) | kassza retries lookups automatically |
|
|
50
|
+
| `network` / `timeout` | Transport failure; the outcome is unknown for writes | Look up by `orderNumber` before trying again |
|
|
51
|
+
| `configuration` | Missing key, bad options | No |
|
|
52
|
+
| `unexpected_response` | Számlázz.hu answered in an unknown format | Report it |
|