kassza 0.1.0 → 0.1.1
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/README.md +472 -69
- package/assets/comparison.svg +39 -0
- package/assets/logo-wordmark.svg +2 -2
- package/package.json +54 -48
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="https://
|
|
2
|
+
<img src="https://raw.githubusercontent.com/futozs/kassza/main/assets/logo-wordmark.svg" alt="kassza" width="460">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
@@ -8,95 +8,357 @@
|
|
|
8
8
|
</p>
|
|
9
9
|
|
|
10
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="
|
|
11
|
+
<a href="https://www.npmjs.com/package/kassza"><img src="https://img.shields.io/npm/v/kassza?color=14532D&label=npm" alt="npm verzió"></a>
|
|
12
|
+
<a href="https://www.npmjs.com/package/kassza"><img src="https://img.shields.io/npm/dm/kassza?color=14532D&label=letöltés" alt="havi letöltés"></a>
|
|
13
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/
|
|
14
|
+
<img src="https://img.shields.io/badge/TypeScript-szigorú-14532D" alt="TypeScript">
|
|
15
|
+
<img src="https://img.shields.io/npm/l/kassza?color=14532D&label=licenc" alt="MIT licenc">
|
|
15
16
|
</p>
|
|
16
17
|
|
|
17
18
|
```bash
|
|
18
19
|
npm i kassza
|
|
19
20
|
```
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
```ts
|
|
23
|
+
import { createKassza } from 'kassza'
|
|
24
|
+
|
|
25
|
+
const kassza = createKassza()
|
|
26
|
+
|
|
27
|
+
const szamla = await kassza.invoices.create({
|
|
28
|
+
buyer: { name: 'Vevő Kft.', zip: '1111', city: 'Budapest', address: 'Fő utca 1.', email: 'vevo@ceg.hu' },
|
|
29
|
+
items: [{ name: 'Webfejlesztés', quantity: 10, unit: 'óra', netUnitPrice: 15_000, vat: 27 }],
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
console.log(szamla.number, szamla.grossTotal)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Ennyi. A kerekítést, a magyar dátumot, az XML-t, a session cookie-t és a hibakezelést a kassza intézi, a vevő pedig e-mailben megkapja a számlát.
|
|
36
|
+
|
|
37
|
+
## Miért kassza?
|
|
38
|
+
|
|
39
|
+
<p align="center">
|
|
40
|
+
<img src="https://raw.githubusercontent.com/futozs/kassza/main/assets/comparison.svg" alt="Számla Agent műveletek: kassza 11, a többi csomag 2–3" width="720">
|
|
41
|
+
</p>
|
|
42
|
+
|
|
43
|
+
| | **kassza** | szamlazz.js | @ribbery009/ szamlazz-ts | @halftome/ szamlazz-client | szamlazz.ts | szamlazzhu-client |
|
|
44
|
+
|---|:---:|:---:|:---:|:---:|:---:|:---:|
|
|
45
|
+
| Számla, sztornó | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
46
|
+
| Számla adatainak lekérése | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
|
|
47
|
+
| **Nyugta** (létrehozás, sztornó, lekérdezés, kiküldés) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
48
|
+
| **Befizetés rögzítése** | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
49
|
+
| **PDF lekérése utólag** | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
50
|
+
| **Díjbekérő törlése** | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
51
|
+
| **Adószám lekérdezés (NAV)** | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
52
|
+
| Runtime függőség | **0** | 6 | 6 | 1 | 7 | 3 |
|
|
53
|
+
| TypeScript típusok | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ |
|
|
54
|
+
| Edge (Workers, Vercel Edge) | ✅ | ❌ | ❌ | – | ❌ | ❌ |
|
|
55
|
+
| Mock kliens teszteléshez | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
56
|
+
| IPN webhook, validátorok, PDF tárhely | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
57
|
+
|
|
58
|
+
<sub>A műveleteket 2026 szeptemberében a publikált csomagok kódjában ellenőriztük (melyik Agent form mezőt küldik). Az Edge oszlopban ❌ az axios, tough-cookie vagy form-data függőség miatt szerepel, ezek nem futnak Workers alatt. A „–” azt jelenti, hogy nem teszteltük.</sub>
|
|
59
|
+
|
|
60
|
+
## Tartalom
|
|
61
|
+
|
|
62
|
+
- [Beállítás](#beállítás)
|
|
63
|
+
- [Számlák](#számlák)
|
|
64
|
+
- [Nyugták](#nyugták)
|
|
65
|
+
- [Adószám lekérdezés](#adószám-lekérdezés)
|
|
66
|
+
- [Hibakezelés](#hibakezelés)
|
|
67
|
+
- [Fizetési értesítés (IPN)](#fizetési-értesítés-ipn)
|
|
68
|
+
- [PDF mentése tárhelyre](#pdf-mentése-tárhelyre)
|
|
69
|
+
- [Serverless és edge](#serverless-és-edge)
|
|
70
|
+
- [Tesztelés](#tesztelés)
|
|
71
|
+
- [Validátorok és pénzszámítás](#validátorok-és-pénzszámítás)
|
|
72
|
+
- [Haladó beállítások](#haladó-beállítások)
|
|
73
|
+
- [AI-val kódolsz?](#ai-val-kódolsz)
|
|
74
|
+
|
|
75
|
+
## Beállítás
|
|
76
|
+
|
|
77
|
+
Az Agent kulcsot a Számlázz.hu felületén, a vezérlőpult alján generálhatod.
|
|
22
78
|
|
|
23
|
-
|
|
79
|
+
```bash
|
|
80
|
+
SZAMLAZZ_AGENT_KEY=a-te-agent-kulcsod
|
|
81
|
+
```
|
|
24
82
|
|
|
25
83
|
```ts
|
|
26
84
|
import { createKassza } from 'kassza'
|
|
27
85
|
|
|
28
|
-
const kassza = createKassza(
|
|
86
|
+
export const kassza = createKassza()
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Az ismétlődő adatokat elég egyszer megadni:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
export const kassza = createKassza({
|
|
93
|
+
agentKey: process.env.SZAMLAZZ_AGENT_KEY,
|
|
94
|
+
defaults: {
|
|
95
|
+
invoice: {
|
|
96
|
+
prefix: 'WEB',
|
|
97
|
+
paymentDueInDays: 8,
|
|
98
|
+
seller: { emailReplyTo: 'penzugy@ceg.hu', emailSubject: 'Elkészült a számlád' },
|
|
99
|
+
},
|
|
100
|
+
receipt: { prefix: 'NYGT', paymentMethod: 'bankkártya' },
|
|
101
|
+
},
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
await kassza.verifyCredentials()
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
A `verifyCredentials()` visszatérési értéke `true`, ha a kulcs jó.
|
|
108
|
+
|
|
109
|
+
## Számlák
|
|
110
|
+
|
|
111
|
+
### Számla
|
|
29
112
|
|
|
113
|
+
```ts
|
|
30
114
|
const szamla = await kassza.invoices.create({
|
|
31
|
-
|
|
32
|
-
|
|
115
|
+
orderNumber: 'REND-1001',
|
|
116
|
+
paymentMethod: 'bankkártya',
|
|
117
|
+
paid: true,
|
|
118
|
+
buyer: {
|
|
119
|
+
name: 'Vevő Kft.',
|
|
120
|
+
zip: '1111',
|
|
121
|
+
city: 'Budapest',
|
|
122
|
+
address: 'Fő utca 1.',
|
|
123
|
+
email: 'vevo@ceg.hu',
|
|
124
|
+
taxNumber: '12345678-2-42',
|
|
125
|
+
},
|
|
126
|
+
items: [
|
|
127
|
+
{ name: 'Póló', quantity: 2, grossUnitPrice: 5_990, vat: 27 },
|
|
128
|
+
{ name: 'Szállítás', grossUnitPrice: 1_490, vat: 27 },
|
|
129
|
+
],
|
|
33
130
|
})
|
|
34
131
|
|
|
35
132
|
szamla.number
|
|
133
|
+
szamla.netTotal
|
|
36
134
|
szamla.grossTotal
|
|
37
135
|
szamla.pdf
|
|
38
136
|
```
|
|
39
137
|
|
|
40
|
-
|
|
138
|
+
- Az `orderNumber` a saját azonosítód, ezzel később vissza is keresheted a számlát.
|
|
139
|
+
- A `szamla.pdf` egy `Uint8Array`.
|
|
41
140
|
|
|
42
|
-
|
|
141
|
+
Az ár megadható nettóban vagy bruttóban, a kerekítés a hivatalos szabályok szerint történik:
|
|
43
142
|
|
|
44
143
|
```ts
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
await kassza.receipts.send({ receiptNumber: nyugta.number, emails: 'vevo@example.hu' })
|
|
144
|
+
{ name: 'Tanácsadás', netUnitPrice: 20_000, vat: 27 }
|
|
145
|
+
{ name: 'Belépő', grossUnitPrice: 4_990, vat: 27 }
|
|
146
|
+
{ name: 'Könyv', grossUnitPrice: 3_500, vat: 5 }
|
|
147
|
+
{ name: 'Oktatás', netUnitPrice: 50_000, vat: 'AAM' }
|
|
52
148
|
```
|
|
53
149
|
|
|
54
|
-
|
|
150
|
+
A nettó ár a B2B számlákhoz, a bruttó ár a B2C számlákhoz való. Az `'AAM'` alanyi adómentes tételt jelöl.
|
|
151
|
+
|
|
152
|
+
### Díjbekérő, majd számla
|
|
55
153
|
|
|
56
154
|
```ts
|
|
57
155
|
const dijbekero = await kassza.invoices.create({
|
|
58
156
|
type: 'proforma',
|
|
59
|
-
orderNumber: '
|
|
157
|
+
orderNumber: 'NEV-42',
|
|
60
158
|
buyer,
|
|
61
|
-
items
|
|
159
|
+
items,
|
|
62
160
|
})
|
|
63
161
|
|
|
64
|
-
const szamla = await kassza.invoices.create({
|
|
65
|
-
|
|
162
|
+
const szamla = await kassza.invoices.create({
|
|
163
|
+
proformaNumber: dijbekero.number,
|
|
164
|
+
orderNumber: 'NEV-42',
|
|
165
|
+
paid: true,
|
|
166
|
+
buyer,
|
|
167
|
+
items,
|
|
168
|
+
})
|
|
66
169
|
```
|
|
67
170
|
|
|
68
|
-
|
|
171
|
+
### Díjbekérő törlése
|
|
69
172
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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()` |
|
|
173
|
+
```ts
|
|
174
|
+
await kassza.invoices.deleteProforma({ orderNumber: 'NEV-42' })
|
|
175
|
+
```
|
|
83
176
|
|
|
84
|
-
|
|
177
|
+
### Előleg- és végszámla
|
|
85
178
|
|
|
86
179
|
```ts
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
180
|
+
const eloleg = await kassza.invoices.create({
|
|
181
|
+
type: 'advance',
|
|
182
|
+
orderNumber: 'PROJ-7',
|
|
183
|
+
buyer,
|
|
184
|
+
items: [{ name: 'Előleg', netUnitPrice: 300_000, vat: 27 }],
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
await kassza.invoices.create({
|
|
188
|
+
type: 'final',
|
|
189
|
+
advanceInvoiceNumber: eloleg.number,
|
|
190
|
+
orderNumber: 'PROJ-7',
|
|
191
|
+
buyer,
|
|
192
|
+
items: [
|
|
193
|
+
{ name: 'Weboldal', netUnitPrice: 1_000_000, vat: 27 },
|
|
194
|
+
{ name: 'Előleg levonása', quantity: -1, netUnitPrice: 300_000, vat: 27 },
|
|
195
|
+
],
|
|
196
|
+
})
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Helyesbítő számla
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
await kassza.invoices.create({
|
|
203
|
+
type: 'corrective',
|
|
204
|
+
correctedInvoiceNumber: 'E-WEB-2026-12',
|
|
205
|
+
buyer,
|
|
206
|
+
items: [{ name: 'Póló visszáru', quantity: -1, grossUnitPrice: 5_990, vat: 27 }],
|
|
207
|
+
})
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Szállítólevél
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
await kassza.invoices.create({ type: 'deliveryNote', buyer, items })
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Devizás számla, külföldi vevő
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
await kassza.invoices.create({
|
|
220
|
+
currency: 'EUR',
|
|
221
|
+
language: 'en',
|
|
222
|
+
buyer: {
|
|
223
|
+
name: 'Acme GmbH',
|
|
224
|
+
country: 'Germany',
|
|
225
|
+
zip: '10115',
|
|
226
|
+
city: 'Berlin',
|
|
227
|
+
address: 'Hauptstr. 1',
|
|
228
|
+
euTaxNumber: 'DE123456789',
|
|
229
|
+
taxpayerType: 'euBusiness',
|
|
91
230
|
},
|
|
231
|
+
items: [{ name: 'Consulting', quantity: 8, unit: 'hour', netUnitPrice: 95, vat: 'EUFAD37' }],
|
|
232
|
+
})
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Az árfolyam, ha nem adod meg, automatikusan az MNB aktuális árfolyama.
|
|
236
|
+
|
|
237
|
+
### Előnézet, számla kiállítása nélkül
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
const { pdf, grossTotal } = await kassza.invoices.preview({ buyer, items })
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Melléklet az e-mailhez
|
|
244
|
+
|
|
245
|
+
```ts
|
|
246
|
+
await kassza.invoices.create({
|
|
247
|
+
buyer,
|
|
248
|
+
items,
|
|
249
|
+
attachments: [{ filename: 'aszf.pdf', content: aszfPdfBytes, contentType: 'application/pdf' }],
|
|
92
250
|
})
|
|
93
251
|
```
|
|
94
252
|
|
|
95
|
-
|
|
253
|
+
Legfeljebb 5 melléklet adható meg, darabonként 2 MB.
|
|
254
|
+
|
|
255
|
+
### Sztornó
|
|
256
|
+
|
|
257
|
+
```ts
|
|
258
|
+
const sztorno = await kassza.invoices.reverse('E-WEB-2026-12')
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Befizetés rögzítése
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
await kassza.invoices.registerPayment({ invoiceNumber: 'E-WEB-2026-12', amount: 12_700 })
|
|
265
|
+
|
|
266
|
+
await kassza.invoices.registerPayment({
|
|
267
|
+
invoiceNumber: 'E-WEB-2026-13',
|
|
268
|
+
payments: [
|
|
269
|
+
{ method: 'készpénz', amount: 5_000, date: '2026-09-01' },
|
|
270
|
+
{ method: 'átutalás', amount: 7_700 },
|
|
271
|
+
],
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
await kassza.invoices.clearPayments('E-WEB-2026-12')
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
A `registerPayment` egyetlen befizetést és több részletet is fogad. A `clearPayments` az összes befizetést törli a számláról.
|
|
278
|
+
|
|
279
|
+
### PDF letöltése utólag
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
import { writeFile } from 'node:fs/promises'
|
|
283
|
+
|
|
284
|
+
const { pdf } = await kassza.invoices.getPdf('E-WEB-2026-12')
|
|
285
|
+
await writeFile('szamla.pdf', pdf)
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Rendelésszám vagy külső azonosító alapján is működik: `getPdf({ orderNumber: 'REND-1001' })`, `getPdf({ externalId: 'a1b2' })`.
|
|
96
289
|
|
|
97
|
-
|
|
290
|
+
### Számla adatainak lekérése
|
|
98
291
|
|
|
99
|
-
|
|
292
|
+
```ts
|
|
293
|
+
const adatok = await kassza.invoices.get({ orderNumber: 'REND-1001' })
|
|
294
|
+
adatok.header.issueDate
|
|
295
|
+
adatok.buyer.name
|
|
296
|
+
adatok.items
|
|
297
|
+
adatok.payments
|
|
298
|
+
|
|
299
|
+
const talan = await kassza.invoices.find({ orderNumber: 'REND-9999' })
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
A `find` `null`-t ad, ha nincs ilyen számla, a `get` ilyenkor hibát dob.
|
|
303
|
+
|
|
304
|
+
## Nyugták
|
|
305
|
+
|
|
306
|
+
### Nyugta
|
|
307
|
+
|
|
308
|
+
```ts
|
|
309
|
+
const nyugta = await kassza.receipts.create({
|
|
310
|
+
prefix: 'NYGT',
|
|
311
|
+
paymentMethod: 'készpénz',
|
|
312
|
+
callId: 'KASSZA-2026-0001',
|
|
313
|
+
items: [
|
|
314
|
+
{ name: 'Kávé', quantity: 2, grossUnitPrice: 890, vat: 27 },
|
|
315
|
+
{ name: 'Kifli', grossUnitPrice: 250, vat: 5 },
|
|
316
|
+
],
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
nyugta.number
|
|
320
|
+
nyugta.totals.grossAmount
|
|
321
|
+
nyugta.pdf
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
A `callId` véd a dupla nyugta ellen.
|
|
325
|
+
|
|
326
|
+
### Kiküldés e-mailben
|
|
327
|
+
|
|
328
|
+
```ts
|
|
329
|
+
await kassza.receipts.send({ receiptNumber: nyugta.number, emails: 'vevo@ceg.hu' })
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Lekérdezés
|
|
333
|
+
|
|
334
|
+
```ts
|
|
335
|
+
const ugyanaz = await kassza.receipts.get(nyugta.number)
|
|
336
|
+
const rendelesbol = await kassza.receipts.find({ orderNumber: 'REND-1001' })
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### Sztornó
|
|
340
|
+
|
|
341
|
+
```ts
|
|
342
|
+
await kassza.receipts.reverse(nyugta.number)
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
## Adószám lekérdezés
|
|
346
|
+
|
|
347
|
+
```ts
|
|
348
|
+
const ceg = await kassza.taxpayer.query('13421739')
|
|
349
|
+
|
|
350
|
+
if (ceg.valid) {
|
|
351
|
+
ceg.name
|
|
352
|
+
ceg.taxNumber?.formatted
|
|
353
|
+
ceg.address?.formatted
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
A cégadatok a NAV-tól jönnek, a cím formázva, például `1031 Budapest, Záhony utca 7.`
|
|
358
|
+
|
|
359
|
+
## Hibakezelés
|
|
360
|
+
|
|
361
|
+
Minden hiba `SzamlazzError`, magyar üzenettel és javítási tippel.
|
|
100
362
|
|
|
101
363
|
```ts
|
|
102
364
|
import { isSzamlazzError } from 'kassza'
|
|
@@ -105,46 +367,187 @@ try {
|
|
|
105
367
|
await kassza.invoices.create(input)
|
|
106
368
|
} catch (error) {
|
|
107
369
|
if (!isSzamlazzError(error)) throw error
|
|
108
|
-
|
|
109
|
-
error.
|
|
110
|
-
error.hint
|
|
111
|
-
error
|
|
370
|
+
|
|
371
|
+
if (error.isDuplicate) return kassza.invoices.find({ orderNumber: input.orderNumber! })
|
|
372
|
+
if (error.category === 'validation') console.error(error.message, error.hint)
|
|
373
|
+
throw error
|
|
112
374
|
}
|
|
113
375
|
```
|
|
114
376
|
|
|
115
|
-
|
|
377
|
+
- `error.code`: a Számlázz.hu hibakódja, például `57`.
|
|
378
|
+
- `error.hint`: magyar javítási tipp.
|
|
379
|
+
|
|
380
|
+
| `error.category` | Jelentés |
|
|
381
|
+
|---|---|
|
|
382
|
+
| `validation` | Hibás adat, a kérés el sem ment, vagy a Számlázz.hu elutasította |
|
|
383
|
+
| `duplicate` | Ez a rendelésszám vagy `callId` már létezik |
|
|
384
|
+
| `partial_success` | A számla elkészült, csak az e-mail nem ment ki. **Ne állítsd ki újra!** |
|
|
385
|
+
| `not_found` | Nincs ilyen bizonylat |
|
|
386
|
+
| `auth` / `account` | Rossz kulcs, lejárt előfizetés |
|
|
387
|
+
| `network` / `timeout` / `maintenance` | Átmeneti hiba |
|
|
116
388
|
|
|
117
|
-
|
|
389
|
+
Újraküldés csak ott történik magától, ahol biztonságos: lekérdezéseknél, hálózati hibánál, legfeljebb 5-ször. **Számlát a kassza soha nem küld újra.** A Számlázz.hu kitiltja azt, aki ciklusban próbálkozik. Bizonytalan hiba után a `find({ orderNumber })` megmondja, elkészült-e a számla.
|
|
118
390
|
|
|
119
|
-
##
|
|
391
|
+
## Fizetési értesítés (IPN)
|
|
392
|
+
|
|
393
|
+
A Számlázz.hu értesít, ha egy számlát kifizettek. Például egy Next.js route handlerben:
|
|
120
394
|
|
|
121
395
|
```ts
|
|
122
|
-
import {
|
|
396
|
+
import { ipnOkResponse, readIpnNotification } from 'kassza/ipn'
|
|
397
|
+
|
|
398
|
+
export async function POST(request: Request) {
|
|
399
|
+
const ipn = await readIpnNotification(request)
|
|
400
|
+
|
|
401
|
+
if (ipn.isFullyPaid) await rendelesFizetve(ipn.orderNumber, ipn.paidAmount)
|
|
402
|
+
|
|
403
|
+
return ipnOkResponse()
|
|
404
|
+
}
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
Csak a Számlázz.hu IP-címeiről jövő kérést érdemes elfogadni:
|
|
408
|
+
|
|
409
|
+
```ts
|
|
410
|
+
import { isSzamlazzIp } from 'kassza/ipn'
|
|
411
|
+
|
|
412
|
+
isSzamlazzIp(request.headers.get('x-forwarded-for') ?? '')
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
## PDF mentése tárhelyre
|
|
416
|
+
|
|
417
|
+
S3, Cloudflare R2, MinIO vagy Backblaze esetén nem kell az AWS SDK:
|
|
418
|
+
|
|
419
|
+
```ts
|
|
420
|
+
import { invoicePdfKey, s3FetchStorage, storePdf } from 'kassza/storage'
|
|
421
|
+
|
|
422
|
+
const tarhely = s3FetchStorage({
|
|
423
|
+
bucket: 'szamlak',
|
|
424
|
+
region: 'auto',
|
|
425
|
+
endpoint: 'https://<account-id>.r2.cloudflarestorage.com',
|
|
426
|
+
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
|
|
427
|
+
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
|
|
428
|
+
})
|
|
123
429
|
|
|
124
|
-
const kassza = createMockKassza()
|
|
125
430
|
const szamla = await kassza.invoices.create(input)
|
|
126
|
-
|
|
127
|
-
|
|
431
|
+
const fajl = await storePdf(tarhely, invoicePdfKey({ number: szamla.number }), szamla.pdf!)
|
|
432
|
+
|
|
433
|
+
fajl.key
|
|
128
434
|
```
|
|
129
435
|
|
|
130
|
-
A
|
|
436
|
+
A kulcs például `szamlak/2026/09/E-WEB-2026-12.pdf` lesz.
|
|
131
437
|
|
|
132
|
-
|
|
438
|
+
Fájlrendszerbe (Node):
|
|
133
439
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
440
|
+
```ts
|
|
441
|
+
import { fsStorage } from 'kassza/storage/fs'
|
|
442
|
+
|
|
443
|
+
const tarhely = fsStorage({ directory: './szamlak' })
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
További adapterek: `s3Storage` (AWS SDK), `r2BindingStorage`, `vercelBlobStorage`, `uploadthingStorage`, `supabaseStorage`, `memoryStorage`.
|
|
447
|
+
|
|
448
|
+
## Serverless és edge
|
|
449
|
+
|
|
450
|
+
A session cookie újrahasznosítása gyorsítja a hívásokat. Serverless környezetben tedd közös tárolóba:
|
|
451
|
+
|
|
452
|
+
```ts
|
|
453
|
+
import { Redis } from '@upstash/redis'
|
|
454
|
+
import { upstashRedisCookieStore } from 'kassza/cookie-stores'
|
|
455
|
+
|
|
456
|
+
const kassza = createKassza({ cookieStore: upstashRedisCookieStore(Redis.fromEnv()) })
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
Cloudflare Workers alatt:
|
|
460
|
+
|
|
461
|
+
```ts
|
|
462
|
+
import { cloudflareKvCookieStore } from 'kassza/cookie-stores'
|
|
463
|
+
|
|
464
|
+
const kassza = createKassza({ agentKey: env.SZAMLAZZ_AGENT_KEY, cookieStore: cloudflareKvCookieStore(env.KASSZA_KV) })
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
Van még `ioredisCookieStore`, `nodeRedisCookieStore` és `customCookieStore` is. Ha a tároló elérhetetlen, a számlázás attól még működik.
|
|
468
|
+
|
|
469
|
+
## Tesztelés
|
|
470
|
+
|
|
471
|
+
A mock kliens valódi API-hívás nélkül működik, de ugyanazt a validációt és kerekítést futtatja, mint az éles kliens.
|
|
472
|
+
|
|
473
|
+
```ts
|
|
474
|
+
import { createMockKassza } from 'kassza/testing'
|
|
475
|
+
|
|
476
|
+
test('fizetés után számlát állít ki', async () => {
|
|
477
|
+
const kassza = createMockKassza()
|
|
478
|
+
|
|
479
|
+
await fizetesKezelo(rendeles, kassza)
|
|
480
|
+
|
|
481
|
+
expect(kassza.calls.map((call) => call.method)).toEqual(['invoices.create'])
|
|
482
|
+
expect([...kassza.invoiceRecords.values()][0]?.details.totals.grossAmount).toBe(12_700)
|
|
483
|
+
})
|
|
484
|
+
|
|
485
|
+
test('hálózati hibánál nem számláz kétszer', async () => {
|
|
486
|
+
const kassza = createMockKassza()
|
|
487
|
+
kassza.failNext('invoices.create')
|
|
488
|
+
|
|
489
|
+
await expect(fizetesKezelo(rendeles, kassza)).rejects.toThrow()
|
|
490
|
+
})
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
## Validátorok és pénzszámítás
|
|
494
|
+
|
|
495
|
+
```ts
|
|
496
|
+
import {
|
|
497
|
+
isValidHungarianBankAccount,
|
|
498
|
+
isValidHungarianTaxNumber,
|
|
499
|
+
parseHungarianAddress,
|
|
500
|
+
} from 'kassza/validators'
|
|
501
|
+
|
|
502
|
+
isValidHungarianTaxNumber('13421739-2-41')
|
|
503
|
+
isValidHungarianBankAccount('11773016-11111018')
|
|
504
|
+
parseHungarianAddress('1031 Budapest, Záhony utca 7.')
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
A `parseHungarianAddress` eredménye `{ zip: '1031', city: 'Budapest', address: 'Záhony utca 7.' }`. Ezen kívül van `isValidHungarianIban`, `isValidHungarianZipCode`, `isValidEuVatNumber` és `isValidEmail` is.
|
|
508
|
+
|
|
509
|
+
```ts
|
|
510
|
+
import { calculateInvoiceItem, calculateReceiptItem } from 'kassza/money'
|
|
511
|
+
|
|
512
|
+
calculateInvoiceItem({ quantity: 3, grossUnitPrice: 500, vat: 27 })
|
|
513
|
+
calculateReceiptItem({ grossUnitPrice: 1_000, vat: 27 })
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
Az első eredménye `{ netAmount: 1181, vatAmount: 319, grossAmount: 1500, ... }`, a másodiké `{ netAmount: 787.4, vatAmount: 212.6, grossAmount: 1000, ... }`.
|
|
517
|
+
|
|
518
|
+
## Haladó beállítások
|
|
519
|
+
|
|
520
|
+
```ts
|
|
521
|
+
const kassza = createKassza({
|
|
522
|
+
timeoutMs: 30_000,
|
|
523
|
+
maxAttempts: 3,
|
|
524
|
+
hooks: {
|
|
525
|
+
onResponse: ({ action, status, durationMs }) => logger.info({ action, status, durationMs }),
|
|
526
|
+
onError: ({ action, error, willRetry }) => logger.warn({ action, code: error.code, willRetry }),
|
|
527
|
+
},
|
|
528
|
+
})
|
|
529
|
+
|
|
530
|
+
await kassza.invoices.getPdf('E-WEB-2026-12', { signal: AbortSignal.timeout(5_000) })
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
- A `maxAttempts` csak a lekérdezésekre vonatkozik, és legfeljebb 5 lehet.
|
|
534
|
+
- A hookok soha nem kapják meg az Agent kulcsot.
|
|
535
|
+
- Minden metódus fogad `AbortSignal`-t.
|
|
536
|
+
|
|
537
|
+
A `resetSession()` új sessiont kér. Hívd meg, ha a Számlázz.hu fiókban megváltoztak a cégadatok:
|
|
538
|
+
|
|
539
|
+
```ts
|
|
540
|
+
await kassza.resetSession()
|
|
541
|
+
```
|
|
143
542
|
|
|
144
543
|
## AI-val kódolsz?
|
|
145
544
|
|
|
146
|
-
A csomagban van egy `agents/` mappa
|
|
545
|
+
A csomagban van egy `agents/` mappa. Ebbő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. Szólj rá az AI-ra:
|
|
546
|
+
|
|
547
|
+
> Használd a kassza csomagot, és előbb olvasd el a `node_modules/kassza/agents/README.md`-t.
|
|
548
|
+
|
|
549
|
+
Claude Code-hoz kész skill is van: másold a `node_modules/kassza/agents/skills/kassza` mappát a projekted `.claude/skills/` mappájába.
|
|
147
550
|
|
|
148
551
|
---
|
|
149
552
|
|
|
150
|
-
Nem hivatalos csomag, nem kapcsolódik a KBOSS.hu Kft.-hez (Számlázz.hu).
|
|
553
|
+
<sub>Nem hivatalos csomag, nem kapcsolódik a KBOSS.hu Kft.-hez (Számlázz.hu). A hivatalos dokumentáció a <a href="https://docs.szamlazz.hu/hu/agent/">docs.szamlazz.hu</a> oldalon érhető el. MIT licenc.</sub>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="760" height="344" viewBox="0 0 760 344" role="img" aria-labelledby="t d">
|
|
2
|
+
<title id="t">Számla Agent műveletek, 11-ből</title>
|
|
3
|
+
<desc id="d">kassza 11, szamlazz.js 3, @ribbery009/szamlazz-ts 3, @halftome/szamlazz-client 3, szamlazz.ts 3, szamlazzhu-client 2</desc>
|
|
4
|
+
<style>
|
|
5
|
+
text{font-family:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif}
|
|
6
|
+
.title{fill:#111827;font-size:22px;font-weight:700}.sub{fill:#6B7280;font-size:14px}
|
|
7
|
+
.label{fill:#374151;font-size:15px}.label.hi{fill:#111827;font-weight:700}
|
|
8
|
+
.value{fill:#374151;font-size:15px;font-variant-numeric:tabular-nums}.value.hi{fill:#111827;font-weight:700}
|
|
9
|
+
.bar{fill:#9CA3AF}.bar.hi{fill:#14532D}.track{fill:#E5E7EB}.card{fill:#FFFFFF;stroke:#E5E7EB}
|
|
10
|
+
@media (prefers-color-scheme:dark){.title,.label.hi,.value.hi{fill:#F9FAFB}.sub{fill:#9CA3AF}.label,.value{fill:#D1D5DB}.bar{fill:#6B7280}.bar.hi{fill:#22C55E}.track{fill:#1F2937}.card{fill:#0D1117;stroke:#30363D}}
|
|
11
|
+
</style>
|
|
12
|
+
<rect class="card" x="0.5" y="0.5" width="759" height="343" rx="12"/>
|
|
13
|
+
<text class="title" x="16" y="34">Számla Agent műveletek, 11-ből</text>
|
|
14
|
+
<text class="sub" x="16" y="58">A csomagok publikált kódjában ellenőrizve, 2026. szeptember</text>
|
|
15
|
+
<text class="label hi" x="256" y="106" text-anchor="end">kassza</text>
|
|
16
|
+
<rect class="track" x="272" y="92" width="374" height="18" rx="4"/>
|
|
17
|
+
<rect class="bar hi" x="272" y="92" width="374" height="18" rx="4"/>
|
|
18
|
+
<text class="value hi" x="658" y="106">11/11</text>
|
|
19
|
+
<text class="label" x="256" y="144" text-anchor="end">szamlazz.js</text>
|
|
20
|
+
<rect class="track" x="272" y="130" width="374" height="18" rx="4"/>
|
|
21
|
+
<rect class="bar" x="272" y="130" width="102" height="18" rx="4"/>
|
|
22
|
+
<text class="value" x="658" y="144">3/11</text>
|
|
23
|
+
<text class="label" x="256" y="182" text-anchor="end">@ribbery009/szamlazz-ts</text>
|
|
24
|
+
<rect class="track" x="272" y="168" width="374" height="18" rx="4"/>
|
|
25
|
+
<rect class="bar" x="272" y="168" width="102" height="18" rx="4"/>
|
|
26
|
+
<text class="value" x="658" y="182">3/11</text>
|
|
27
|
+
<text class="label" x="256" y="220" text-anchor="end">@halftome/szamlazz-client</text>
|
|
28
|
+
<rect class="track" x="272" y="206" width="374" height="18" rx="4"/>
|
|
29
|
+
<rect class="bar" x="272" y="206" width="102" height="18" rx="4"/>
|
|
30
|
+
<text class="value" x="658" y="220">3/11</text>
|
|
31
|
+
<text class="label" x="256" y="258" text-anchor="end">szamlazz.ts</text>
|
|
32
|
+
<rect class="track" x="272" y="244" width="374" height="18" rx="4"/>
|
|
33
|
+
<rect class="bar" x="272" y="244" width="102" height="18" rx="4"/>
|
|
34
|
+
<text class="value" x="658" y="258">3/11</text>
|
|
35
|
+
<text class="label" x="256" y="296" text-anchor="end">szamlazzhu-client</text>
|
|
36
|
+
<rect class="track" x="272" y="282" width="374" height="18" rx="4"/>
|
|
37
|
+
<rect class="bar" x="272" y="282" width="68" height="18" rx="4"/>
|
|
38
|
+
<text class="value" x="658" y="296">2/11</text>
|
|
39
|
+
</svg>
|
package/assets/logo-wordmark.svg
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="320" viewBox="0 0 1200 320" role="img" aria-labelledby="title">
|
|
2
2
|
<title id="title">kassza</title>
|
|
3
|
-
<style>.
|
|
3
|
+
<style>.tag{fill:#6B7280}</style>
|
|
4
4
|
<defs>
|
|
5
5
|
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
|
|
6
6
|
<stop offset="0" stop-color="#14532D"/>
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
<rect x="160" y="368" width="120" height="12" rx="6" fill="#14532D" opacity=".28"/>
|
|
20
20
|
<rect x="304" y="368" width="48" height="12" rx="6" fill="#F59E0B"/>
|
|
21
21
|
</g>
|
|
22
|
-
<text x="340" y="196" font-family="ui-rounded, 'SF Pro Rounded', 'Nunito', 'Inter', system-ui, sans-serif" font-size="168" font-weight="800" letter-spacing="-6"
|
|
22
|
+
<text x="340" y="196" font-family="ui-rounded, 'SF Pro Rounded', 'Nunito', 'Inter', system-ui, sans-serif" font-size="168" font-weight="800" letter-spacing="-6" fill="url(#bg)">kassza</text>
|
|
23
23
|
<rect x="348" y="226" width="96" height="14" rx="7" fill="#F59E0B"/>
|
|
24
24
|
<text x="464" y="240" font-family="ui-monospace, 'SF Mono', 'JetBrains Mono', monospace" font-size="30" font-weight="500" class="tag">Számlázz.hu, TypeScriptben</text>
|
|
25
25
|
</svg>
|
package/package.json
CHANGED
|
@@ -1,43 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kassza",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"private": false,
|
|
5
|
-
"description": "Számlázz.hu Számla Agent kliens TypeScripthez: számla, díjbekérő, nyugta, sztornó, befizetés, PDF, adószám. 0 függőség. Nem hivatalos.",
|
|
6
|
-
"keywords": [
|
|
7
|
-
"billing",
|
|
8
|
-
"dijbekero",
|
|
9
|
-
"e-szamla",
|
|
10
|
-
"hungary",
|
|
11
|
-
"invoice",
|
|
12
|
-
"ipn",
|
|
13
|
-
"kassza",
|
|
14
|
-
"nav",
|
|
15
|
-
"nav-online-szamla",
|
|
16
|
-
"nyugta",
|
|
17
|
-
"receipt",
|
|
18
|
-
"szamla",
|
|
19
|
-
"szamla agent",
|
|
20
|
-
"szamla-agent",
|
|
21
|
-
"szamlazz",
|
|
22
|
-
"szamlazz.hu",
|
|
23
|
-
"typescript"
|
|
24
|
-
],
|
|
25
|
-
"license": "MIT",
|
|
3
|
+
"version": "0.1.1",
|
|
26
4
|
"author": "futozs",
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"node": ">=22"
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/futozs/szamlazz-egyszerubben.git"
|
|
31
8
|
},
|
|
32
|
-
"files": [
|
|
33
|
-
"dist",
|
|
34
|
-
"agents",
|
|
35
|
-
"assets",
|
|
36
|
-
"llms.txt"
|
|
37
|
-
],
|
|
38
9
|
"main": "./dist/index.cjs",
|
|
39
10
|
"module": "./dist/index.js",
|
|
40
|
-
"
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"@arethetypeswrong/cli": "^0.18.5",
|
|
13
|
+
"@biomejs/biome": "^2.5.13",
|
|
14
|
+
"@types/node": "^26.6.0",
|
|
15
|
+
"@vitest/coverage-v8": "^5.0.1",
|
|
16
|
+
"publint": "^0.3.24",
|
|
17
|
+
"tsdown": "^0.23.0",
|
|
18
|
+
"typescript": "^7.0.2",
|
|
19
|
+
"vitest": "^5.0.1"
|
|
20
|
+
},
|
|
41
21
|
"exports": {
|
|
42
22
|
".": {
|
|
43
23
|
"import": {
|
|
@@ -121,6 +101,40 @@
|
|
|
121
101
|
},
|
|
122
102
|
"./package.json": "./package.json"
|
|
123
103
|
},
|
|
104
|
+
"bugs": {
|
|
105
|
+
"url": "https://github.com/futozs/szamlazz-egyszerubben/issues"
|
|
106
|
+
},
|
|
107
|
+
"description": "Számlázz.hu Számla Agent kliens TypeScripthez: számla, díjbekérő, nyugta, sztornó, befizetés, PDF, adószám. 0 függőség. Nem hivatalos.",
|
|
108
|
+
"engines": {
|
|
109
|
+
"node": ">=22"
|
|
110
|
+
},
|
|
111
|
+
"files": [
|
|
112
|
+
"dist",
|
|
113
|
+
"agents",
|
|
114
|
+
"assets",
|
|
115
|
+
"llms.txt"
|
|
116
|
+
],
|
|
117
|
+
"homepage": "https://github.com/futozs/szamlazz-egyszerubben#readme",
|
|
118
|
+
"keywords": [
|
|
119
|
+
"billing",
|
|
120
|
+
"dijbekero",
|
|
121
|
+
"e-szamla",
|
|
122
|
+
"hungary",
|
|
123
|
+
"invoice",
|
|
124
|
+
"ipn",
|
|
125
|
+
"kassza",
|
|
126
|
+
"nav",
|
|
127
|
+
"nav-online-szamla",
|
|
128
|
+
"nyugta",
|
|
129
|
+
"receipt",
|
|
130
|
+
"szamla",
|
|
131
|
+
"szamla agent",
|
|
132
|
+
"szamla-agent",
|
|
133
|
+
"szamlazz",
|
|
134
|
+
"szamlazz.hu",
|
|
135
|
+
"typescript"
|
|
136
|
+
],
|
|
137
|
+
"license": "MIT",
|
|
124
138
|
"scripts": {
|
|
125
139
|
"build": "tsdown",
|
|
126
140
|
"dev": "tsdown --watch",
|
|
@@ -132,20 +146,12 @@
|
|
|
132
146
|
"lint:fix": "biome check --write .",
|
|
133
147
|
"check:package": "publint && attw --pack . --profile node16",
|
|
134
148
|
"ci": "npm run lint && npm run typecheck && npm run test:coverage && npm run build && npm run check:package",
|
|
135
|
-
"changeset": "changeset",
|
|
136
149
|
"xsd:fetch": "node scripts/fetch-xsd.mjs",
|
|
137
|
-
"prepublishOnly": "npm run ci"
|
|
150
|
+
"prepublishOnly": "npm run ci",
|
|
151
|
+
"release": "node scripts/release.mjs",
|
|
152
|
+
"release:dry": "node scripts/release.mjs --dry-run"
|
|
138
153
|
},
|
|
139
|
-
"
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
"@changesets/cli": "^3.0.3",
|
|
143
|
-
"@types/node": "^26.6.0",
|
|
144
|
-
"@vitest/coverage-v8": "^5.0.1",
|
|
145
|
-
"publint": "^0.3.24",
|
|
146
|
-
"tsdown": "^0.23.0",
|
|
147
|
-
"typescript": "^7.0.2",
|
|
148
|
-
"vitest": "^5.0.1"
|
|
149
|
-
},
|
|
150
|
-
"homepage": "https://www.npmjs.com/package/kassza"
|
|
154
|
+
"sideEffects": false,
|
|
155
|
+
"type": "module",
|
|
156
|
+
"types": "./dist/index.d.ts"
|
|
151
157
|
}
|