@kreiseck/kasseneck-api 0.27.2 → 0.28.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.
Files changed (50) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/README.md +59 -9
  3. package/dist/cjs/partner/ablauf.d.ts +4 -3
  4. package/dist/cjs/partner/ablauf.js +4 -3
  5. package/dist/cjs/partner/api.d.ts +20 -8
  6. package/dist/cjs/partner/api.js +12 -1
  7. package/dist/cjs/partner/endpunkte.d.ts +9 -12
  8. package/dist/cjs/partner/endpunkte.js +85 -26
  9. package/dist/cjs/partner/fehler.d.ts +1 -1
  10. package/dist/cjs/partner/fehler.js +8 -3
  11. package/dist/cjs/partner/index.d.ts +7 -3
  12. package/dist/cjs/partner/index.js +7 -2
  13. package/dist/cjs/partner/typen.d.ts +144 -34
  14. package/dist/cjs/partner/typen.js +4 -0
  15. package/dist/cjs/partner/webhooks.d.ts +76 -14
  16. package/dist/cjs/partner/webhooks.js +40 -12
  17. package/dist/cjs/payments/index.d.ts +5 -4
  18. package/dist/cjs/payments/index.js +4 -3
  19. package/dist/cjs/rechnung/summen.d.ts +18 -3
  20. package/dist/cjs/rechnung/summen.js +18 -3
  21. package/dist/cjs/rechnung/typen.d.ts +1 -1
  22. package/dist/cjs/rechnung/vertrag.d.ts +1 -1
  23. package/dist/cjs/rechnung/vertrag.js +1 -1
  24. package/dist/esm/partner/ablauf.d.ts +4 -3
  25. package/dist/esm/partner/ablauf.js +4 -3
  26. package/dist/esm/partner/api.d.ts +20 -8
  27. package/dist/esm/partner/api.js +11 -1
  28. package/dist/esm/partner/endpunkte.d.ts +9 -12
  29. package/dist/esm/partner/endpunkte.js +85 -26
  30. package/dist/esm/partner/fehler.d.ts +1 -1
  31. package/dist/esm/partner/fehler.js +8 -3
  32. package/dist/esm/partner/index.d.ts +7 -3
  33. package/dist/esm/partner/index.js +5 -1
  34. package/dist/esm/partner/typen.d.ts +144 -34
  35. package/dist/esm/partner/typen.js +4 -0
  36. package/dist/esm/partner/webhooks.d.ts +76 -14
  37. package/dist/esm/partner/webhooks.js +40 -12
  38. package/dist/esm/payments/index.d.ts +5 -4
  39. package/dist/esm/payments/index.js +4 -3
  40. package/dist/esm/rechnung/summen.d.ts +18 -3
  41. package/dist/esm/rechnung/summen.js +18 -3
  42. package/dist/esm/rechnung/typen.d.ts +1 -1
  43. package/dist/esm/rechnung/vertrag.d.ts +1 -1
  44. package/dist/esm/rechnung/vertrag.js +1 -1
  45. package/fixtures/hobex-hps-codes.json +1 -1
  46. package/fixtures/kasse-texte.json +1 -1
  47. package/fixtures/oberflaeche.json +3 -1
  48. package/fixtures/rechnung-api.schema.json +1 -1
  49. package/fixtures/rechnung-texte.json +1 -1
  50. package/package.json +1 -1
@@ -12,6 +12,10 @@
12
12
  * einfuehrt, soll diesen Client nicht zum Absturz bringen, sondern ihn
13
13
  * durchreichen. Eingabetypen sind dagegen eng — ein Tippfehler soll ein
14
14
  * Compilerfehler sein und keine `validation`-Antwort vom Server.
15
+ *
16
+ * **Die Formen sind die der `/v3`** (seit 0.28.0): Feldnamen und Werte, auf
17
+ * die ein Programm verzweigt, sind englisch; Texte fuer Menschen (`message`,
18
+ * `note`, `statusText`, `nextSteps`) bleiben deutsch, die Fehlercodes ebenso.
15
19
  */
16
20
  import type { KasseneckSecret } from './secret.js';
17
21
  /**
@@ -67,9 +71,29 @@ export interface PartnerInfo {
67
71
  };
68
72
  apps: PartnerApp[];
69
73
  }
70
- export type Rechtsform = 'einzel' | 'eu' | 'og' | 'kg' | 'gmbh' | 'gmbhcokg' | 'ag' | 'verein' | 'sonstige';
71
- export type Bundesland = 'burgenland' | 'kaernten' | 'niederoesterreich' | 'oberoesterreich' | 'salzburg' | 'steiermark' | 'tirol' | 'vorarlberg' | 'wien';
72
- export type KontaktRolle = 'geschaeftsfuehrung' | 'buchhaltung' | 'technik' | 'kasse';
74
+ /**
75
+ * Rechtsform des Betriebs, so wie `/v3` sie schreibt und liest.
76
+ *
77
+ * Die oesterreichischen Kurzformen (`eu`, `og`, `kg`, `gmbh`, `gmbhcokg`, `ag`)
78
+ * bleiben wie sie sind; nur die drei Woerter, die es auf Englisch gibt, sind
79
+ * englisch. `/v3` weist die deutschen Werte aus `/v1` (`einzel`, `verein`,
80
+ * `sonstige`) mit `validation` ab, statt sie still zu uebersetzen.
81
+ */
82
+ export type LegalForm = 'sole_proprietor' | 'eu' | 'og' | 'kg' | 'gmbh' | 'gmbhcokg' | 'ag' | 'association' | 'other';
83
+ /** @deprecated Seit 0.28.0 dasselbe wie [LegalForm] (englische Werte der `/v3`). */
84
+ export type Rechtsform = LegalForm;
85
+ /**
86
+ * Bundesland als ISO-3166-2-Code: `AT-1` Burgenland, `AT-2` Kaernten,
87
+ * `AT-3` Niederoesterreich, `AT-4` Oberoesterreich, `AT-5` Salzburg,
88
+ * `AT-6` Steiermark, `AT-7` Tirol, `AT-8` Vorarlberg, `AT-9` Wien.
89
+ */
90
+ export type AustrianState = 'AT-1' | 'AT-2' | 'AT-3' | 'AT-4' | 'AT-5' | 'AT-6' | 'AT-7' | 'AT-8' | 'AT-9';
91
+ /** @deprecated Seit 0.28.0 dasselbe wie [AustrianState] (ISO-Codes der `/v3`). */
92
+ export type Bundesland = AustrianState;
93
+ /** Rolle einer Kontaktperson; `/v1` sagte `geschaeftsfuehrung`, `buchhaltung`, `technik`, `kasse`. */
94
+ export type ContactRole = 'management' | 'accounting' | 'technical' | 'pos';
95
+ /** @deprecated Seit 0.28.0 dasselbe wie [ContactRole] (englische Werte der `/v3`). */
96
+ export type KontaktRolle = ContactRole;
73
97
  export interface BetriebAdresse {
74
98
  street: string;
75
99
  /** Hausnummer, kurz und alphanumerisch: `49`, `12a`, `49/5`. */
@@ -82,8 +106,11 @@ export interface BetriebSteuer {
82
106
  /** Steuernummer im Format `12-345/6789`; die Pruefziffer wird geprueft. */
83
107
  taxNumber: string;
84
108
  smallBusiness: boolean;
85
- /** UID, z. B. `ATU12345675`. */
86
- uid?: string;
109
+ /**
110
+ * UID, z. B. `ATU12345675`. Heisst auf der Leitung `vatId` (auch schon unter
111
+ * `/v1`); ein `uid` wies der Server als unbekanntes Feld ab.
112
+ */
113
+ vatId?: string;
87
114
  /** GLN, 13 Ziffern. */
88
115
  gln?: string;
89
116
  }
@@ -91,7 +118,7 @@ export interface BetriebKontakt {
91
118
  name: string;
92
119
  email: string;
93
120
  phone?: string;
94
- roles?: KontaktRolle[];
121
+ roles?: ContactRole[];
95
122
  }
96
123
  export interface BetriebSteuerberater {
97
124
  name: string;
@@ -115,11 +142,11 @@ export interface BetriebSteuerberater {
115
142
  */
116
143
  export interface Betrieb {
117
144
  companyName: string;
118
- legalForm: Rechtsform;
145
+ legalForm: LegalForm;
119
146
  /** Anmeldung des Betriebs im Kasseneck-Panel; darf dort noch keinen Zugang haben. */
120
147
  email: string;
121
148
  address: BetriebAdresse;
122
- state: Bundesland;
149
+ state: AustrianState;
123
150
  taxDetails: BetriebSteuer;
124
151
  /** Mindestens einer, hoechstens zehn. */
125
152
  contacts: BetriebKontakt[];
@@ -171,6 +198,20 @@ export interface CreateCustomerOptions {
171
198
  env?: PartnerEnv;
172
199
  }
173
200
  export type KundenStatus = 'created' | 'fon_configured' | 'signature_requested' | 'signature_ready' | 'cashregister_created' | 'live' | 'blocked' | (string & {});
201
+ /** Abrechnungsrhythmus eines Entgelts; `/v1` sagte `monat`, `jahr`, `einmal`. */
202
+ export type FeeInterval = 'monthly' | 'yearly' | 'once';
203
+ /**
204
+ * Das Entgelt, das mit einem Aufruf gebucht wurde: nur dann in der Antwort,
205
+ * wenn die Konditionen des Partners dafuer einen Preis vorsehen. Unter `/v1`
206
+ * hiess es `entgelt` mit `rhythmus`.
207
+ */
208
+ export interface PartnerFee {
209
+ /** Betrag in ganzen Cent. */
210
+ cents: number;
211
+ interval: FeeInterval | (string & {});
212
+ /** `true` fuer einen Testbetrieb: gebucht, aber nicht verrechnet. */
213
+ test: boolean;
214
+ }
174
215
  export interface CreateCustomerResult {
175
216
  customerId: string;
176
217
  status: KundenStatus;
@@ -182,24 +223,48 @@ export interface CreateCustomerResult {
182
223
  sentTo: string | null;
183
224
  };
184
225
  nextSteps: string[];
226
+ /** Das gebuchte Entgelt; `null`, wenn die Konditionen keinen Preis dafuer vorsehen. */
227
+ fee: PartnerFee | null;
185
228
  /** `true`, wenn derselbe `idempotencyKey` schon einmal ankam. */
186
229
  replayed: boolean;
187
230
  }
188
231
  /**
189
- * Stand des Auftragsverarbeitungsvertrags eines Betriebs.
232
+ * Wie das Partnerkonto den AVV handhabt; `/v1` sagte `direkt`, `vollmacht`,
233
+ * `unterauftrag`.
234
+ */
235
+ export type AvvMode = 'direct' | 'power_of_attorney' | 'subprocessor';
236
+ /**
237
+ * Stand eines Vertrags des Betriebs mit Kasseneck: `pending` (noch nicht
238
+ * bestaetigt), `confirmed`, `outdated` (eine neuere Pflichtfassung ist zu
239
+ * bestaetigen) oder `not_required` (Testumgebung).
190
240
  *
191
- * **Vertraege wirken im Partner-Weg nicht mehr** (Stand 2026-08-31): keine
192
- * Antwort fuehrt dieses Feld, kein Schritt in `naechsteSchritte` verlangt
193
- * einen Vertrag, und eine Kasse geht deswegen nicht weniger live. Der Typ
194
- * bleibt, damit eine Antwort, die ihn doch noch traegt, lesbar durchkommt —
195
- * **vorausgesetzt wird er nirgends**. Fuer selbst registrierte Kunden gibt es
196
- * die Maschinerie weiterhin, aber nicht ueber diese Schnittstelle.
241
+ * **Die Vertraege wirken:** live geht ohne beide (AVV und Nutzungsvertrag)
242
+ * keine Kasse live, `activateCashregister` antwortet dann `vertrag_offen`. Der
243
+ * Betrieb bestaetigt sie selbst ueber den Einrichtungs-Link
244
+ * (`sendPartnerCustomerFonLink`); die Ereignisse `customer.avv_accepted` und
245
+ * `customer.terms_accepted` melden die Bestaetigung. In der Testumgebung sind
246
+ * sie nicht noetig.
197
247
  */
198
- export interface AvvStand {
199
- status: string;
248
+ export interface VertragStand {
249
+ status: 'pending' | 'confirmed' | 'outdated' | 'not_required' | (string & {});
200
250
  version: string | null;
201
251
  confirmedAt: number | null;
202
- mode: string | null;
252
+ }
253
+ /**
254
+ * Stand des Auftragsverarbeitungsvertrags (AVV, Art. 28 DSGVO). Zusaetzlich zu
255
+ * [VertragStand] der Status `via_partner` (der Partnervertrag deckt den AVV,
256
+ * Weg `subprocessor`) und `mode`, der Weg des Partnerkontos.
257
+ */
258
+ export interface AvvStand extends VertragStand {
259
+ status: VertragStand['status'] | 'via_partner';
260
+ mode: AvvMode | (string & {}) | null;
261
+ }
262
+ /** FinanzOnline-Stand in der Liste: ist der Link draussen, geoeffnet, der Zugang geprueft? */
263
+ export interface KundenFonStand {
264
+ configured: boolean;
265
+ linkSentAt: number | null;
266
+ /** Erste Oeffnung; wird mit einem Ersatz-Link zurueckgesetzt. */
267
+ linkOpenedAt: number | null;
203
268
  }
204
269
  export interface KundenZeile {
205
270
  customerId: string;
@@ -208,12 +273,15 @@ export interface KundenZeile {
208
273
  appId: string | null;
209
274
  env: PartnerEnv;
210
275
  createdAt: number | null;
276
+ /** FinanzOnline-Stand; `null`, wenn die Antwort ihn nicht fuehrt. */
277
+ fon: KundenFonStand | null;
211
278
  /**
212
- * Vertragsstand, falls die Antwort ihn ueberhaupt fuehrt — heute tut sie das
213
- * nicht, der Wert ist dann `null`. Siehe [AvvStand]: nichts in diesem Client
214
- * setzt ihn voraus.
279
+ * AVV-Stand; `null`, wenn die Antwort ihn nicht fuehrt. Kein erfundenes
280
+ * `pending`: "nicht mitgeliefert" und "nicht bestaetigt" sind zweierlei.
215
281
  */
216
282
  avv: AvvStand | null;
283
+ /** Stand des Nutzungsvertrags; `null`, wenn die Antwort ihn nicht fuehrt. */
284
+ terms: VertragStand | null;
217
285
  }
218
286
  export interface ListCustomersOptions {
219
287
  status?: KundenStatus;
@@ -233,9 +301,10 @@ export interface Kunde extends KundenZeile {
233
301
  createdAt: number | null;
234
302
  createdVia: string | null;
235
303
  business: Record<string, unknown>;
236
- fon: {
237
- configured: boolean;
304
+ /** In der Einzelsicht zusaetzlich: wann geprueft, an welche (maskierte) Adresse der Link ging. */
305
+ fon: KundenFonStand & {
238
306
  verifiedAt: number | null;
307
+ linkSentTo: string | null;
239
308
  };
240
309
  access: {
241
310
  email: string | null;
@@ -250,22 +319,28 @@ export interface FonLinkResult {
250
319
  expiresAt: number;
251
320
  }
252
321
  /**
253
- * `beantragt → zugeteilt → registriert → bereit`. `registriert` heisst: die
254
- * Einheit ist FinanzOnline bekannt; `bereit` heisst: sie darf signieren. In der
255
- * Testumgebung wird ohne `registriert` direkt `bereit` erreicht.
322
+ * `requested → assigned → registered → ready`. `registered` heisst: die
323
+ * Einheit ist FinanzOnline bekannt; `ready` heisst: sie darf signieren. In der
324
+ * Testumgebung wird ohne `registered` direkt `ready` erreicht.
256
325
  */
257
326
  export type SignaturAntragStatus = 'requested' | 'assigned' | 'registered' | 'ready' | 'failed' | 'cancelled' | (string & {});
327
+ /**
328
+ * Gruende in der Historie eines Signaturantrags. `card_entered` und
329
+ * `finanzonline` hiessen unter `/v1` `karte_eingetragen` und `fon`.
330
+ */
331
+ export type SignatureHistoryReason = 'api' | 'portal' | 'card_entered' | 'finanzonline' | 'automation_off' | 'test_environment' | 'no_stock' | (string & {});
258
332
  export interface SignaturHistorieEintrag {
259
- von: string | null;
260
- nach: string;
333
+ from: SignaturAntragStatus | null;
334
+ to: SignaturAntragStatus;
261
335
  at: number;
262
- reason: string | null;
336
+ reason: SignatureHistoryReason | null;
263
337
  }
264
338
  export interface SignaturAntrag {
265
339
  requestId: string;
266
340
  status: SignaturAntragStatus;
267
341
  statusText: string;
268
- art: string;
342
+ /** Art der Signatureinheit; heute nur `signature_card`. */
343
+ kind: string;
269
344
  vdaId: string | null;
270
345
  signatureId: string | null;
271
346
  error: {
@@ -278,18 +353,53 @@ export interface SignaturAntrag {
278
353
  updatedAt: number | null;
279
354
  history: SignaturHistorieEintrag[];
280
355
  }
356
+ export interface RequestSignatureOptions {
357
+ /** Art der Signatureinheit; heute nur `signature_card` (Vorgabe). */
358
+ kind?: string;
359
+ /** `true` beantragt eine WEITERE Signatur, obwohl schon eine besteht. */
360
+ additional?: boolean;
361
+ }
281
362
  export interface RequestSignatureResult {
282
363
  request: SignaturAntrag;
283
364
  /** `true`, wenn schon ein Antrag lief — dann ist es der laufende. */
284
365
  replayed: boolean;
285
366
  note: string | null;
367
+ /** Das gebuchte Entgelt; `null`, wenn die Konditionen keinen Preis dafuer vorsehen. */
368
+ fee: PartnerFee | null;
369
+ }
370
+ /**
371
+ * Eine Signatur des Betriebs, einzeln. Ein Betrieb kann mehrere haben
372
+ * (Ersatzkarte, zweiter Standort); `signatureRequestId` ist die Kennung, auf die
373
+ * sich eine Kasse beruft.
374
+ */
375
+ export interface CustomerSignature {
376
+ signatureRequestId: string;
377
+ status: SignaturAntragStatus | 'decommissioned';
378
+ statusText: string;
379
+ inProgress: boolean;
380
+ ready: boolean;
381
+ kind: string;
382
+ vdaId: string | null;
383
+ requestId: string | null;
384
+ signatureId: string | null;
385
+ error: {
386
+ code: string | null;
387
+ message: string | null;
388
+ rc: string | null;
389
+ } | null;
390
+ createdAt: number | null;
391
+ updatedAt: number | null;
286
392
  }
287
393
  export interface SignaturStand {
288
- signatur: {
394
+ customerId: string;
395
+ /** Die Kurzform: hat der Betrieb ueberhaupt eine brauchbare Signatur? */
396
+ signature: {
289
397
  ready: boolean;
290
398
  signatureId: string | null;
291
399
  vdaId: string | null;
292
400
  };
401
+ /** Jede Signatur einzeln. */
402
+ signatures: CustomerSignature[];
293
403
  requests: SignaturAntrag[];
294
404
  fon: {
295
405
  present: boolean;
@@ -297,8 +407,8 @@ export interface SignaturStand {
297
407
  };
298
408
  }
299
409
  /** Die Schritte der Inbetriebnahme, in dieser Reihenfolge. */
300
- export type KassenSchritt = 'signatur' | 'register_cashregister' | 'start_receipt' | 'transmit_start_receipt' | (string & {});
301
- export type KassenStatus = 'draft' | 'laeuft' | 'live' | 'failed' | (string & {});
410
+ export type KassenSchritt = 'signature' | 'register_cashregister' | 'start_receipt' | 'transmit_start_receipt' | (string & {});
411
+ export type KassenStatus = 'draft' | 'in_progress' | 'live' | 'failed' | (string & {});
302
412
  export interface Kasse {
303
413
  cashregisterId: string;
304
414
  name: string | null;
@@ -351,7 +461,7 @@ export interface CreateCashregisterResult {
351
461
  started: boolean;
352
462
  ok: boolean | null;
353
463
  step: KassenSchritt | null;
354
- /** `signature_not_ready` oder `automatik_aus`, wenn nicht gestartet wurde. */
464
+ /** `signature_not_ready` oder `automation_off`, wenn nicht gestartet wurde. */
355
465
  reason: string | null;
356
466
  };
357
467
  }
@@ -12,6 +12,10 @@
12
12
  * einfuehrt, soll diesen Client nicht zum Absturz bringen, sondern ihn
13
13
  * durchreichen. Eingabetypen sind dagegen eng — ein Tippfehler soll ein
14
14
  * Compilerfehler sein und keine `validation`-Antwort vom Server.
15
+ *
16
+ * **Die Formen sind die der `/v3`** (seit 0.28.0): Feldnamen und Werte, auf
17
+ * die ein Programm verzweigt, sind englisch; Texte fuer Menschen (`message`,
18
+ * `note`, `statusText`, `nextSteps`) bleiben deutsch, die Fehlercodes ebenso.
15
19
  */
16
20
  // ---------------------------------------------------------------------------
17
21
  // Partner und Schluessel
@@ -11,11 +11,11 @@ import { type VerifyWebhookOptions, type WebhookVerifyReason } from './webhook-s
11
11
  * Alle Ereignisse, die ein Webhook abonnieren **und proben** kann. Ein
12
12
  * Endpunkt bekommt ausschliesslich die, die in seiner `events`-Liste stehen.
13
13
  *
14
- * Kasseneck fuehrt daneben interne Ereignisse (etwa den Abschluss eines
15
- * Auftragsverarbeitungsvertrags). Sie stehen hier bewusst nicht: sie lassen
16
- * sich weder abonnieren noch mit [sendPartnerWebhookTest] ausloesen, und ein
17
- * Name in dieser Liste, den niemand bestellen kann, waere ein Versprechen ohne
18
- * Deckung.
14
+ * **Noch nicht in der Liste:** `customer.avv_accepted` und
15
+ * `customer.terms_accepted`. Das Backend bietet sie inzwischen zum Abonnieren
16
+ * an; die Liste wird mit dem Dart-Zwilling gemeinsam erweitert, weil beide
17
+ * gegeneinander geprueft werden. Abonnieren geht trotzdem schon (`events`
18
+ * nimmt jeden Namen), die Nutzlast beschreibt [ContractAcceptedEventData].
19
19
  */
20
20
  export declare const PARTNER_WEBHOOK_EVENTS: readonly ["customer.created", "customer.updated", "customer.status_changed", "customer.fon_verified", "customer.live_enabled", "signature.requested", "signature.ready", "signature.failed", "cashregister.created", "cashregister.live", "cashregister.failed", "app.version.accepted", "app.version.rejected", "webhook.test"];
21
21
  export type PartnerWebhookEventType = typeof PARTNER_WEBHOOK_EVENTS[number];
@@ -58,6 +58,38 @@ export interface PartnerWebhookEvent<T = Record<string, unknown>> {
58
58
  test: boolean;
59
59
  data: T;
60
60
  }
61
+ /**
62
+ * Die Sprache der Nutzlast eines Webhooks. Ein unter `/v1` angelegter Webhook
63
+ * spricht `v1` (deutsche Werte), ein unter `/v3` angelegter `v3`. Umstellen
64
+ * geht nur vorwaerts, mit `updatePartnerWebhook(id, { apiVersion: 'v3' })`;
65
+ * zurueck auf `v1` gibt es absichtlich nicht.
66
+ *
67
+ * Die Sprache der **Antworten** folgt dagegen dem Pfad: dieser Client ruft
68
+ * `/v3` und bekommt englische Antworten, gleich welche `apiVersion` ein
69
+ * Webhook hat.
70
+ */
71
+ export type WebhookApiVersion = 'v1' | 'v3';
72
+ /** Art des Vertrags in `customer.avv_accepted` / `customer.terms_accepted`. */
73
+ export type ContractKind = 'avv' | 'terms';
74
+ /**
75
+ * Wo der Betrieb bestaetigt hat. Unter `v1` hiessen die ersten fuenf
76
+ * `einrichten`, `prozess`, `partner_vollmacht`, `admin_papier`,
77
+ * `papier_upload`; `app` und `portal` sind in beiden Sprachen gleich.
78
+ */
79
+ export type ContractSource = 'setup_link' | 'process_link' | 'partner_power_of_attorney' | 'admin_paper' | 'paper_upload' | 'app' | 'portal' | (string & {});
80
+ /**
81
+ * Nutzlast von `customer.avv_accepted` und `customer.terms_accepted` in der
82
+ * Sprache `v3`. Ein Webhook mit `apiVersion: 'v1'` schickt hier weiter
83
+ * `kind: 'nutzung'` und die deutschen Quellen.
84
+ */
85
+ export interface ContractAcceptedEventData {
86
+ customerId: string;
87
+ companyName: string;
88
+ kind: ContractKind;
89
+ version: string;
90
+ confirmedAt: number;
91
+ source: ContractSource;
92
+ }
61
93
  export type WebhookEventResult = {
62
94
  ok: true;
63
95
  event: PartnerWebhookEvent;
@@ -82,14 +114,23 @@ export type WebhookEventResult = {
82
114
  * 30 min, 2 h, 12 h) und gilt dann als fehlgeschlagen.
83
115
  */
84
116
  export declare function parseWebhookEvent(optionen: VerifyWebhookOptions): Promise<WebhookEventResult>;
117
+ /** Stand einer Zustellung; `/v1` sagte `offen`, `zugestellt`, `fehlgeschlagen`, `verworfen`. */
118
+ export type WebhookDeliveryStatus = 'pending' | 'delivered' | 'failed' | 'dropped';
85
119
  export interface PartnerWebhook {
86
120
  webhookId: string;
121
+ /** Sprache der Nutzlast; fehlt sie in der Antwort, ist es ein Bestands-Webhook und damit `v1`. */
122
+ apiVersion: WebhookApiVersion | (string & {});
87
123
  url: string;
88
124
  events: string[];
89
125
  active: boolean;
90
126
  description: string | null;
91
127
  createdAt: number | null;
92
- lastDelivery: number | null;
128
+ /** Die letzte Zustellung; `null`, solange keine versucht wurde. */
129
+ lastDelivery: {
130
+ at: number | null;
131
+ status: WebhookDeliveryStatus | (string & {});
132
+ statusCode: number | null;
133
+ } | null;
93
134
  /** Fehlversuche in Folge — steigt der Wert, stimmt beim Empfaenger etwas nicht. */
94
135
  consecutiveFailures: number;
95
136
  }
@@ -120,6 +161,17 @@ export interface WebhookPatch {
120
161
  events?: (PartnerWebhookEventType | (string & {}))[];
121
162
  description?: string;
122
163
  active?: boolean;
164
+ /**
165
+ * Stellt die Nutzlast auf Englisch um. Nur `'v3'` ist moeglich: zurueck auf
166
+ * `v1` geht nicht, damit ein Partner nicht versehentlich wieder deutsche
167
+ * Nutzlasten bekommt.
168
+ */
169
+ apiVersion?: 'v3';
170
+ }
171
+ export interface DeleteWebhookResult {
172
+ webhookId: string;
173
+ /** Unter `/v1` hiess das Feld `geloescht`. */
174
+ deleted: boolean;
123
175
  }
124
176
  export interface WebhookListe {
125
177
  webhooks: PartnerWebhook[];
@@ -134,11 +186,11 @@ export interface WebhookZustellung {
134
186
  webhookId: string;
135
187
  event: string;
136
188
  eventId: string;
137
- /** `offen`, `zugestellt` oder `fehlgeschlagen`. */
138
- status: string;
189
+ /** `dropped`: der Webhook wurde vor der Faelligkeit deaktiviert oder geloescht. */
190
+ status: WebhookDeliveryStatus | (string & {});
139
191
  attempts: number;
140
- letzterVersuchAt: number | null;
141
- naechsterVersuchAt: number | null;
192
+ lastAttemptAt: number | null;
193
+ nextAttemptAt: number | null;
142
194
  statusCode: number | null;
143
195
  /** Auszug der Antwort des Empfaengers, hoechstens 500 Zeichen. */
144
196
  response: string | null;
@@ -171,13 +223,23 @@ export declare function listPartnerWebhooks(rufen: InternerTransport): Promise<W
171
223
  * tun.
172
224
  */
173
225
  export declare function updatePartnerWebhook(rufen: InternerTransport, webhookId: string, patch: WebhookPatch): Promise<PartnerWebhook>;
174
- /** Loescht einen Endpunkt. Danach kommt dort nichts mehr an. */
175
- export declare function deletePartnerWebhook(rufen: InternerTransport, webhookId: string): Promise<string>;
226
+ /**
227
+ * Loescht einen Endpunkt. Danach kommt dort nichts mehr an; offene
228
+ * Zustellungen werden verworfen (`dropped`).
229
+ */
230
+ export declare function deletePartnerWebhook(rufen: InternerTransport, webhookId: string): Promise<DeleteWebhookResult>;
231
+ /** Eine Zustellung der Probe, so wie `sendPartnerWebhookTest` sie meldet. */
232
+ export interface WebhookTestZustellung {
233
+ deliveryId: string;
234
+ webhookId: string;
235
+ status: WebhookDeliveryStatus | (string & {});
236
+ statusCode: number | null;
237
+ }
176
238
  export interface WebhookTestResult {
177
239
  eventId: string;
178
240
  /** Welches Ereignis geprobt wurde — ohne Angabe `webhook.test`. */
179
- ereignis: string;
180
- deliveries: unknown[];
241
+ event: string;
242
+ deliveries: WebhookTestZustellung[];
181
243
  }
182
244
  /**
183
245
  * Schickt eine Probe an genau diesen Endpunkt.
@@ -14,11 +14,11 @@ import { verifyWebhookSignature, } from './webhook-signatur.js';
14
14
  * Alle Ereignisse, die ein Webhook abonnieren **und proben** kann. Ein
15
15
  * Endpunkt bekommt ausschliesslich die, die in seiner `events`-Liste stehen.
16
16
  *
17
- * Kasseneck fuehrt daneben interne Ereignisse (etwa den Abschluss eines
18
- * Auftragsverarbeitungsvertrags). Sie stehen hier bewusst nicht: sie lassen
19
- * sich weder abonnieren noch mit [sendPartnerWebhookTest] ausloesen, und ein
20
- * Name in dieser Liste, den niemand bestellen kann, waere ein Versprechen ohne
21
- * Deckung.
17
+ * **Noch nicht in der Liste:** `customer.avv_accepted` und
18
+ * `customer.terms_accepted`. Das Backend bietet sie inzwischen zum Abonnieren
19
+ * an; die Liste wird mit dem Dart-Zwilling gemeinsam erweitert, weil beide
20
+ * gegeneinander geprueft werden. Abonnieren geht trotzdem schon (`events`
21
+ * nimmt jeden Namen), die Nutzlast beschreibt [ContractAcceptedEventData].
22
22
  */
23
23
  export const PARTNER_WEBHOOK_EVENTS = [
24
24
  'customer.created',
@@ -100,16 +100,30 @@ export async function parseWebhookEvent(optionen) {
100
100
  function objekt(wert) {
101
101
  return wert !== null && typeof wert === 'object' && !Array.isArray(wert) ? wert : {};
102
102
  }
103
+ function letzteZustellung(wert) {
104
+ if (wert === null || typeof wert !== 'object' || Array.isArray(wert))
105
+ return null;
106
+ const z = wert;
107
+ return {
108
+ at: typeof z['at'] === 'number' ? z['at'] : null,
109
+ status: typeof z['status'] === 'string' ? z['status'] : '',
110
+ statusCode: typeof z['statusCode'] === 'number' ? z['statusCode'] : null,
111
+ };
112
+ }
103
113
  function webhook(eintrag) {
104
114
  const w = objekt(eintrag);
105
115
  return {
106
116
  webhookId: typeof w['webhookId'] === 'string' ? w['webhookId'] : '',
117
+ // Fehlt das Feld, ist es ein Bestands-Webhook: der spricht v1. Kein
118
+ // `v3` aus Kulanz, sonst verzweigt ein Empfaenger auf englische Werte,
119
+ // die nie kommen.
120
+ apiVersion: typeof w['apiVersion'] === 'string' && w['apiVersion'] ? w['apiVersion'] : 'v1',
107
121
  url: typeof w['url'] === 'string' ? w['url'] : '',
108
122
  events: Array.isArray(w['events']) ? w['events'].filter((e) => typeof e === 'string') : [],
109
123
  active: w['active'] !== false,
110
124
  description: typeof w['description'] === 'string' ? w['description'] : null,
111
125
  createdAt: typeof w['createdAt'] === 'number' ? w['createdAt'] : null,
112
- lastDelivery: typeof w['lastDelivery'] === 'number' ? w['lastDelivery'] : null,
126
+ lastDelivery: letzteZustellung(w['lastDelivery']),
113
127
  consecutiveFailures: typeof w['consecutiveFailures'] === 'number' ? w['consecutiveFailures'] : 0,
114
128
  };
115
129
  }
@@ -186,13 +200,19 @@ export async function updatePartnerWebhook(rufen, webhookId, patch) {
186
200
  const daten = objekt(await rufen('updatePartnerWebhook', { webhookId: id, patch }));
187
201
  return webhook(daten['webhook']);
188
202
  }
189
- /** Loescht einen Endpunkt. Danach kommt dort nichts mehr an. */
203
+ /**
204
+ * Loescht einen Endpunkt. Danach kommt dort nichts mehr an; offene
205
+ * Zustellungen werden verworfen (`dropped`).
206
+ */
190
207
  export async function deletePartnerWebhook(rufen, webhookId) {
191
208
  const id = typeof webhookId === 'string' ? webhookId.trim() : '';
192
209
  if (!id)
193
210
  throw new KasseneckValidationError('deletePartnerWebhook', 'webhookId fehlt', 'request');
194
211
  const daten = objekt(await rufen('deletePartnerWebhook', { webhookId: id }));
195
- return typeof daten['webhookId'] === 'string' ? daten['webhookId'] : id;
212
+ return {
213
+ webhookId: typeof daten['webhookId'] === 'string' ? daten['webhookId'] : id,
214
+ deleted: daten['deleted'] === true,
215
+ };
196
216
  }
197
217
  /**
198
218
  * Schickt eine Probe an genau diesen Endpunkt.
@@ -222,8 +242,16 @@ export async function sendPartnerWebhookTest(rufen, webhookId, event) {
222
242
  const daten = objekt(await rufen('sendPartnerWebhookTest', { webhookId: id, event: ereignis || undefined }));
223
243
  return {
224
244
  eventId: typeof daten['eventId'] === 'string' ? daten['eventId'] : '',
225
- ereignis: typeof daten['ereignis'] === 'string' ? daten['ereignis'] : ereignis || 'webhook.test',
226
- deliveries: Array.isArray(daten['deliveries']) ? daten['deliveries'] : [],
245
+ event: typeof daten['event'] === 'string' ? daten['event'] : ereignis || 'webhook.test',
246
+ deliveries: (Array.isArray(daten['deliveries']) ? daten['deliveries'] : []).map((eintrag) => {
247
+ const z = objekt(eintrag);
248
+ return {
249
+ deliveryId: typeof z['deliveryId'] === 'string' ? z['deliveryId'] : '',
250
+ webhookId: typeof z['webhookId'] === 'string' ? z['webhookId'] : id,
251
+ status: typeof z['status'] === 'string' ? z['status'] : '',
252
+ statusCode: typeof z['statusCode'] === 'number' ? z['statusCode'] : null,
253
+ };
254
+ }),
227
255
  };
228
256
  }
229
257
  /**
@@ -247,8 +275,8 @@ export async function listPartnerWebhookDeliveries(rufen, optionen = {}) {
247
275
  eventId: txt(z['eventId']) ?? '',
248
276
  status: txt(z['status']) ?? '',
249
277
  attempts: zahl(z['attempts']) ?? 0,
250
- letzterVersuchAt: zahl(z['letzterVersuchAt']),
251
- naechsterVersuchAt: zahl(z['naechsterVersuchAt']),
278
+ lastAttemptAt: zahl(z['lastAttemptAt']),
279
+ nextAttemptAt: zahl(z['nextAttemptAt']),
252
280
  statusCode: zahl(z['statusCode']),
253
281
  response: txt(z['response']),
254
282
  createdAt: zahl(z['createdAt']),
@@ -22,9 +22,10 @@
22
22
  * Terminal spricht. Das ist kein Widerspruch zur Umgebungsgrenze, sondern ein
23
23
  * zweiter, seit `hobex-hps.js` genutzter Weg um sie herum.
24
24
  *
25
- * Wer Gutschrift oder Storno am HPS-Terminal braucht, braucht weiterhin die
26
- * Flutter-App: Connect exponiert dafuer (noch) keinen Endpunkt, siehe
27
- * `hobex-hps/connect-client.ts`.
25
+ * Gutschrift und Storno am HPS-Terminal laufen ebenfalls ueber Connect
26
+ * (`POST /v1/terminal/refund` bzw. `/cancel`): `HpsPayments.refund`/`cancel`
27
+ * mit `HpsRefundOptions`/`HpsCancelOptions`, darunter
28
+ * `HpsConnectClient.refund`/`cancel`, siehe `hobex-hps/connect-client.ts`.
28
29
  *
29
30
  * **Kassen-Benutzer-Weg (`registerUserAuth`, Browser-Kasse):** **Keiner** der
30
31
  * vier Cloud-Zahlungs-Endpunkte (Stripe, Hobex-Cloud) setzt `allowRegisterUser`;
@@ -42,4 +43,4 @@
42
43
  export { StripeLinkMode, type StripeLinkModeKey } from '../enums/index.js';
43
44
  export { type CreateStripeLinkOptions, type StripeCaptureResult, createStripeLink, stripeCaptureIntent, } from './stripe.js';
44
45
  export { type HobexPayOptions, type HobexRefundOptions, type HobexTransactionIdOptions, hobexPay, hobexRefund, newHobexTransactionId, } from './hobex.js';
45
- export { type CardPaymentOutcome, type HpsConnectClient, type HpsConnectClientOptions, type HpsConnectFetch, type HpsConnectFetchResponse, type HpsConnectPaymentOptions, type HpsConnectTarget, type HpsConnectTransactionOptions, type HpsCode, type HpsCodeEffect, type HpsCodeReason, type HpsCodeSource, type HpsMeasuredCode, type HpsPaymentEvent, type HpsPaymentEventKind, type HpsPaymentObserver, type HpsPaymentOptions, type HpsPayments, type HpsPaymentResult, type HpsPaymentsOptions, type HpsTransactionIdGeneratorOptions, type HpsTransactionResponse, ABORTED_CODE, APPROVED_CODE, CARD_NOT_PRESENT_CODE, createHpsConnectClient, createHpsPayments, createHpsTransactionIdGenerator, HPS_CODES, HPS_MEASURED_CODES, HPS_REASON_HINTS, hpsCodeInfo, hpsCodeReason, HpsClarifyTimeoutError, HpsConnectException, HpsConnectTerminalError, HpsConnectTransportError, HpsPreflightError, HpsTransactionIdError, INVALID_TRANSACTION_CODE, isApproved, isConclusive, isConclusiveAsStatus, isHostUncertain, isHostUncertainResult, isInProgress, isNoStatement, isNotAbortable, isTechnicalError, isUnknownCode, isValidHpsTransactionId, mayRetrySafely, MAX_TRANSACTION_ID_LENGTH, newHpsTransactionId, NOT_ABORTABLE_CODE, NO_STATEMENT_CODE, parseHpsTransactionResponse, PREFLIGHT_CONNECT_CODES, TECHNICAL_ERROR_CODE, NOT_FOUND_HTTP_STATUS, TERMINAL_BUSY_HTTP_STATUS, TRANSACTION_CANCELED_CODE, } from './hobex-hps/index.js';
46
+ export { type CardPaymentOutcome, type HpsConnectClient, type HpsConnectCancelOptions, type HpsConnectClientOptions, type HpsConnectFetch, type HpsConnectFetchResponse, type HpsConnectPaymentOptions, type HpsConnectRefundOptions, type HpsConnectTarget, type HpsConnectTransactionOptions, type HpsCancelOptions, type HpsCode, type HpsCodeEffect, type HpsCodeReason, type HpsCodeSource, type HpsMeasuredCode, type HpsPaymentEvent, type HpsPaymentEventKind, type HpsPaymentObserver, type HpsPaymentOptions, type HpsPayments, type HpsPaymentResult, type HpsPaymentsOptions, type HpsRefundOptions, type HpsTransactionIdGeneratorOptions, type HpsTransactionResponse, ABORTED_CODE, APPROVED_CODE, CARD_NOT_PRESENT_CODE, createHpsConnectClient, createHpsPayments, createHpsTransactionIdGenerator, HPS_CODES, HPS_MEASURED_CODES, HPS_REASON_HINTS, hpsCodeInfo, hpsCodeReason, HpsClarifyTimeoutError, HpsConnectException, HpsConnectTerminalError, HpsConnectTransportError, HpsPreflightError, HpsTransactionIdError, INVALID_TRANSACTION_CODE, isApproved, isConclusive, isConclusiveAsStatus, isHostUncertain, isHostUncertainResult, isInProgress, isNoStatement, isNotAbortable, isTechnicalError, isUnknownCode, isValidHpsTransactionId, mayRetrySafely, MAX_TRANSACTION_ID_LENGTH, newHpsTransactionId, NOT_ABORTABLE_CODE, NO_STATEMENT_CODE, parseHpsTransactionResponse, PREFLIGHT_CONNECT_CODES, TECHNICAL_ERROR_CODE, NOT_FOUND_HTTP_STATUS, TERMINAL_BUSY_HTTP_STATUS, TRANSACTION_CANCELED_CODE, } from './hobex-hps/index.js';
@@ -22,9 +22,10 @@
22
22
  * Terminal spricht. Das ist kein Widerspruch zur Umgebungsgrenze, sondern ein
23
23
  * zweiter, seit `hobex-hps.js` genutzter Weg um sie herum.
24
24
  *
25
- * Wer Gutschrift oder Storno am HPS-Terminal braucht, braucht weiterhin die
26
- * Flutter-App: Connect exponiert dafuer (noch) keinen Endpunkt, siehe
27
- * `hobex-hps/connect-client.ts`.
25
+ * Gutschrift und Storno am HPS-Terminal laufen ebenfalls ueber Connect
26
+ * (`POST /v1/terminal/refund` bzw. `/cancel`): `HpsPayments.refund`/`cancel`
27
+ * mit `HpsRefundOptions`/`HpsCancelOptions`, darunter
28
+ * `HpsConnectClient.refund`/`cancel`, siehe `hobex-hps/connect-client.ts`.
28
29
  *
29
30
  * **Kassen-Benutzer-Weg (`registerUserAuth`, Browser-Kasse):** **Keiner** der
30
31
  * vier Cloud-Zahlungs-Endpunkte (Stripe, Hobex-Cloud) setzt `allowRegisterUser`;
@@ -1,7 +1,16 @@
1
1
  /**
2
- * Summen einer Rechnung vorab rechnen — genau so, wie der Server sie beim
3
- * Ausstellen rechnet. Fuer Shops, die kassieren, bevor die Rechnung entsteht,
4
- * und denselben Betrag brauchen, den die Rechnung spaeter ausweist.
2
+ * Summen einer Rechnung vorab rechnen, nach der frueheren Formel (Weg 2,
3
+ * Gleitkomma). Gedacht fuer Shops, die kassieren, bevor die Rechnung entsteht.
4
+ *
5
+ * **Veraltet.** Seit dem 23.09.2026, 17:05 Uhr stellt der Server jede neue
6
+ * Rechnung ueber den exakten Ganzzahl-Kern aus (`rechnungRechnen` in
7
+ * `…/rechnung/rechnen`). An Halbcent-Grenzen weicht diese Funktion davon um
8
+ * einen Cent je Satz ab (21,35 EUR netto zu 10 %: hier 23,48 EUR, auf der
9
+ * Rechnung 23,49 EUR). Die Formel bleibt trotzdem, wie sie ist: der Server
10
+ * schaltet den Kern je Konto ueber einen Schalter und rechnet Entwuerfe
11
+ * ausserhalb des Ausstellens teils weiter nach Weg 2, und Backend und
12
+ * Dart-Zwilling pruefen gegen `fixtures/rechnung-summen.json`. Neuer Code
13
+ * nimmt `rechnungRechnen` oder `previewInvoice`.
5
14
  *
6
15
  * Die Regel (je USt-Satz, Betraege in Cent, kaufmaennisch gerundet):
7
16
  *
@@ -38,5 +47,11 @@ export interface SummenPosition {
38
47
  * Server einen steuerfreien Fall ab (etwa eine ig. Lieferung), gehoert dieser
39
48
  * Fall hierher — sonst rechnet die Funktion Steuer, die die Rechnung nicht
40
49
  * ausweist.
50
+ *
51
+ * @deprecated Seit 0.27.3: rechnet nach der frueheren Formel (Weg 2) und kann
52
+ * an Halbcent-Grenzen um einen Cent je Satz von einer heute ausgestellten
53
+ * Rechnung abweichen. Stattdessen `rechnungRechnen` aus
54
+ * `@kreiseck/kasseneck-api/rechnung/rechnen` (Euro-Positionen ueber
55
+ * `positionAusEuro`) oder `previewInvoice` verwenden.
41
56
  */
42
57
  export declare function rechnungSummen(items: readonly SummenPosition[], priceMode: PriceMode, taxScheme?: TaxScheme): InvoiceTotals;