@kreiseck/kasseneck-api 0.6.46 → 0.6.49

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 (102) hide show
  1. package/README.md +95 -15
  2. package/dist/cjs/client/aufrufe.d.ts +1 -1
  3. package/dist/cjs/client/aufrufe.js +1 -0
  4. package/dist/cjs/client/errors.d.ts +7 -1
  5. package/dist/cjs/client/errors.js +8 -1
  6. package/dist/cjs/client/receipts.d.ts +16 -0
  7. package/dist/cjs/client/receipts.js +10 -0
  8. package/dist/cjs/client/transport.js +5 -4
  9. package/dist/cjs/index.d.ts +1 -1
  10. package/dist/cjs/index.js +4 -2
  11. package/dist/cjs/models/cancellation.d.ts +22 -0
  12. package/dist/cjs/models/cancellation.js +31 -1
  13. package/dist/cjs/models/hobex-receipt.js +9 -2
  14. package/dist/cjs/models/index.d.ts +1 -1
  15. package/dist/cjs/models/index.js +3 -1
  16. package/dist/cjs/models/receipt.js +3 -0
  17. package/dist/cjs/models/voucher.js +18 -2
  18. package/dist/cjs/payments/hobex-hps/connect-client.d.ts +104 -0
  19. package/dist/cjs/payments/hobex-hps/connect-client.js +159 -0
  20. package/dist/cjs/payments/hobex-hps/errors.d.ts +122 -0
  21. package/dist/cjs/payments/hobex-hps/errors.js +158 -0
  22. package/dist/cjs/payments/hobex-hps/events.d.ts +22 -0
  23. package/dist/cjs/payments/hobex-hps/events.js +2 -0
  24. package/dist/cjs/payments/hobex-hps/index.d.ts +27 -0
  25. package/dist/cjs/payments/hobex-hps/index.js +66 -0
  26. package/dist/cjs/payments/hobex-hps/outcome.d.ts +31 -0
  27. package/dist/cjs/payments/hobex-hps/outcome.js +7 -0
  28. package/dist/cjs/payments/hobex-hps/payments.d.ts +149 -0
  29. package/dist/cjs/payments/hobex-hps/payments.js +550 -0
  30. package/dist/cjs/payments/hobex-hps/receipt.d.ts +28 -0
  31. package/dist/cjs/payments/hobex-hps/receipt.js +60 -0
  32. package/dist/cjs/payments/hobex-hps/transaction-id.d.ts +65 -0
  33. package/dist/cjs/payments/hobex-hps/transaction-id.js +91 -0
  34. package/dist/cjs/payments/hobex-hps/transaction-response.d.ts +163 -0
  35. package/dist/cjs/payments/hobex-hps/transaction-response.js +245 -0
  36. package/dist/cjs/payments/hobex.js +9 -6
  37. package/dist/cjs/payments/index.d.ts +26 -14
  38. package/dist/cjs/payments/index.js +59 -15
  39. package/dist/cjs/printing/index.d.ts +1 -1
  40. package/dist/cjs/printing/index.js +2 -1
  41. package/dist/cjs/printing/webusb.d.ts +37 -7
  42. package/dist/cjs/printing/webusb.js +76 -18
  43. package/dist/cjs/receipt/epos.d.ts +18 -0
  44. package/dist/cjs/receipt/epos.js +78 -6
  45. package/dist/cjs/receipt/index.d.ts +1 -1
  46. package/dist/cjs/receipt/index.js +2 -1
  47. package/dist/cjs/register/index.d.ts +1 -1
  48. package/dist/cjs/register/index.js +2 -1
  49. package/dist/cjs/register/pairing.d.ts +59 -0
  50. package/dist/cjs/register/pairing.js +51 -2
  51. package/dist/esm/client/aufrufe.d.ts +1 -1
  52. package/dist/esm/client/aufrufe.js +1 -0
  53. package/dist/esm/client/errors.d.ts +7 -1
  54. package/dist/esm/client/errors.js +8 -1
  55. package/dist/esm/client/receipts.d.ts +16 -0
  56. package/dist/esm/client/receipts.js +10 -0
  57. package/dist/esm/client/transport.js +5 -4
  58. package/dist/esm/index.d.ts +1 -1
  59. package/dist/esm/index.js +1 -1
  60. package/dist/esm/models/cancellation.d.ts +22 -0
  61. package/dist/esm/models/cancellation.js +29 -0
  62. package/dist/esm/models/hobex-receipt.js +10 -3
  63. package/dist/esm/models/index.d.ts +1 -1
  64. package/dist/esm/models/index.js +1 -1
  65. package/dist/esm/models/receipt.js +3 -0
  66. package/dist/esm/models/voucher.js +18 -2
  67. package/dist/esm/payments/hobex-hps/connect-client.d.ts +104 -0
  68. package/dist/esm/payments/hobex-hps/connect-client.js +156 -0
  69. package/dist/esm/payments/hobex-hps/errors.d.ts +122 -0
  70. package/dist/esm/payments/hobex-hps/errors.js +149 -0
  71. package/dist/esm/payments/hobex-hps/events.d.ts +22 -0
  72. package/dist/esm/payments/hobex-hps/events.js +1 -0
  73. package/dist/esm/payments/hobex-hps/index.d.ts +27 -0
  74. package/dist/esm/payments/hobex-hps/index.js +26 -0
  75. package/dist/esm/payments/hobex-hps/outcome.d.ts +31 -0
  76. package/dist/esm/payments/hobex-hps/outcome.js +4 -0
  77. package/dist/esm/payments/hobex-hps/payments.d.ts +149 -0
  78. package/dist/esm/payments/hobex-hps/payments.js +547 -0
  79. package/dist/esm/payments/hobex-hps/receipt.d.ts +28 -0
  80. package/dist/esm/payments/hobex-hps/receipt.js +57 -0
  81. package/dist/esm/payments/hobex-hps/transaction-id.d.ts +65 -0
  82. package/dist/esm/payments/hobex-hps/transaction-id.js +86 -0
  83. package/dist/esm/payments/hobex-hps/transaction-response.d.ts +163 -0
  84. package/dist/esm/payments/hobex-hps/transaction-response.js +233 -0
  85. package/dist/esm/payments/hobex.js +9 -6
  86. package/dist/esm/payments/index.d.ts +26 -14
  87. package/dist/esm/payments/index.js +26 -14
  88. package/dist/esm/printing/index.d.ts +1 -1
  89. package/dist/esm/printing/index.js +1 -1
  90. package/dist/esm/printing/webusb.d.ts +37 -7
  91. package/dist/esm/printing/webusb.js +74 -17
  92. package/dist/esm/receipt/epos.d.ts +18 -0
  93. package/dist/esm/receipt/epos.js +76 -6
  94. package/dist/esm/receipt/index.d.ts +1 -1
  95. package/dist/esm/receipt/index.js +1 -1
  96. package/dist/esm/register/index.d.ts +1 -1
  97. package/dist/esm/register/index.js +1 -1
  98. package/dist/esm/register/pairing.d.ts +59 -0
  99. package/dist/esm/register/pairing.js +50 -2
  100. package/fixtures/hobex-hps-codes.json +67 -0
  101. package/fixtures/oberflaeche.json +2 -1
  102. package/package.json +4 -2
@@ -5,6 +5,7 @@ exports.cancelScopeOf = cancelScopeOf;
5
5
  exports.receiptsScopeOf = receiptsScopeOf;
6
6
  exports.pairRegisterDevice = pairRegisterDevice;
7
7
  exports.unpairRegisterDevice = unpairRegisterDevice;
8
+ exports.listRegisterSessionsForDevice = listRegisterSessionsForDevice;
8
9
  exports.listRegisterUsersForDevice = listRegisterUsersForDevice;
9
10
  exports.registerUserLogin = registerUserLogin;
10
11
  exports.registerPinLogin = registerPinLogin;
@@ -119,6 +120,48 @@ async function unpairRegisterDevice(options) {
119
120
  pflicht(name, 'deviceSecret', deviceSecret);
120
121
  await transportFuer(verbindung)(name, { ownerUid, deviceId, deviceSecret }, undefined, [deviceSecret]);
121
122
  }
123
+ /**
124
+ * Welche Sitzungen haelt diese Kasse gerade?
125
+ *
126
+ * Fuer den Anmeldebildschirm: Sind alle Lizenzplaetze belegt, muss eine
127
+ * weichen -- und der Kassier soll sehen, WELCHE, statt dass das Backend still
128
+ * die aelteste nimmt. Die Wahl geht dann als
129
+ * [RegisterUserLoginOptions.takeoverSessionId] mit.
130
+ *
131
+ * Ausgewiesen wird sich ueber das Geraete-Geheimnis, wie bei
132
+ * [listRegisterUsersForDevice]: An diesem Punkt gibt es weder Sitzung noch
133
+ * Token. Die Kasse bestimmt das Backend aus dem Geraet, nicht aus dem Aufruf.
134
+ */
135
+ async function listRegisterSessionsForDevice(options) {
136
+ const { ownerUid, deviceId, deviceSecret, ...verbindung } = options;
137
+ const name = 'listRegisterSessionsForDevice';
138
+ pflicht(name, 'ownerUid', ownerUid);
139
+ pflicht(name, 'deviceId', deviceId);
140
+ pflicht(name, 'deviceSecret', deviceSecret);
141
+ const daten = await transportFuer(verbindung)(name, { ownerUid, deviceId, deviceSecret }, undefined, [deviceSecret]);
142
+ const liste = daten?.sessions;
143
+ if (!Array.isArray(liste)) {
144
+ throw antwortfehler(name, 'Antwort enthaelt keine Sitzungsliste (data.sessions fehlt)');
145
+ }
146
+ const sessions = liste.map((eintrag) => {
147
+ const roh = (typeof eintrag === 'object' && eintrag !== null ? eintrag : {});
148
+ return {
149
+ // Ohne Kennung waere die Zeile nicht waehlbar -- eine Schaltflaeche, die
150
+ // nichts tun kann, ist schlimmer als keine.
151
+ id: pflichtfeld(name, roh, 'id'),
152
+ deviceId: typeof roh['deviceId'] === 'string' && roh['deviceId'] ? roh['deviceId'] : null,
153
+ deviceLabel: text(roh['deviceLabel']) || 'Kasse',
154
+ startedAt: typeof roh['startedAt'] === 'number' ? roh['startedAt'] : null,
155
+ expiresAt: typeof roh['expiresAt'] === 'number' ? roh['expiresAt'] : null,
156
+ selbst: roh['selbst'] === true,
157
+ // Nur uebernehmen, wenn wirklich einer kam: ein leerer String stuende
158
+ // in der Oberflaeche als namenlose Zeile, statt die Spalte wegzulassen.
159
+ ...(typeof roh['userName'] === 'string' && roh['userName'] ? { userName: roh['userName'] } : {}),
160
+ };
161
+ });
162
+ const licenses = typeof daten?.licenses === 'number' && daten.licenses > 0 ? daten.licenses : 1;
163
+ return { licenses, sessions };
164
+ }
122
165
  async function listRegisterUsersForDevice(options) {
123
166
  const { ownerUid, deviceId, deviceSecret, ...verbindung } = options;
124
167
  const name = 'listRegisterUsersForDevice';
@@ -192,7 +235,7 @@ function regel(wert) {
192
235
  * sperren gestaffelt (ab dem fuenften 30 Sekunden, ab dem neunten 15 Minuten).
193
236
  */
194
237
  async function registerUserLogin(options) {
195
- const { ownerUid, deviceId, deviceSecret, userId, pin, cashregisterId, takeover, client, geo, ...verbindung } = options;
238
+ const { ownerUid, deviceId, deviceSecret, userId, pin, cashregisterId, takeover, takeoverSessionId, client, geo, ...verbindung } = options;
196
239
  const name = 'registerUserLogin';
197
240
  pflicht(name, 'ownerUid', ownerUid);
198
241
  pflicht(name, 'deviceId', deviceId);
@@ -210,6 +253,9 @@ async function registerUserLogin(options) {
210
253
  // Nur die ausdrueckliche Uebernahme geht mit: das Backend prueft auf
211
254
  // `=== true`, und ein mitgesendetes `false` waere nur Rauschen.
212
255
  takeover: takeover === true ? true : undefined,
256
+ // Leerer String heisst „nichts gewaehlt" und gehoert nicht in die
257
+ // Nutzlast -- das Backend wiese ihn sonst als unbekannte Sitzung ab.
258
+ takeoverSessionId: takeoverSessionId ? takeoverSessionId : undefined,
213
259
  client, geo: geo ?? undefined,
214
260
  }, undefined, [pin, deviceSecret]);
215
261
  return sitzungAusAntwort(name, daten);
@@ -221,7 +267,7 @@ async function registerUserLogin(options) {
221
267
  * zaehlen und sperren dort am **Geraet**, nicht an einem Benutzer.
222
268
  */
223
269
  async function registerPinLogin(options) {
224
- const { ownerUid, deviceId, deviceSecret, pin, cashregisterId, takeover, client, geo, ...verbindung } = options;
270
+ const { ownerUid, deviceId, deviceSecret, pin, cashregisterId, takeover, takeoverSessionId, client, geo, ...verbindung } = options;
225
271
  const name = 'registerPinLogin';
226
272
  pflicht(name, 'ownerUid', ownerUid);
227
273
  pflicht(name, 'deviceId', deviceId);
@@ -236,6 +282,9 @@ async function registerPinLogin(options) {
236
282
  cashregisterId,
237
283
  // Nur die ausdrueckliche Uebernahme geht mit — wie bei registerUserLogin.
238
284
  takeover: takeover === true ? true : undefined,
285
+ // Leerer String heisst „nichts gewaehlt" und gehoert nicht in die
286
+ // Nutzlast -- das Backend wiese ihn sonst als unbekannte Sitzung ab.
287
+ takeoverSessionId: takeoverSessionId ? takeoverSessionId : undefined,
239
288
  client, geo: geo ?? undefined,
240
289
  }, undefined, [pin, deviceSecret]);
241
290
  return sitzungAusAntwort(name, daten);
@@ -13,7 +13,7 @@
13
13
  * umhuellt, muss ihn weiterhin absetzen koennen.
14
14
  */
15
15
  import type { TransportBodyFields } from './transport.js';
16
- export declare const AUFRUFE: readonly ["cancelReceipt", "createPaymentLinkStripe", "createPrintJob", "createReceipt", "downloadDailyReport", "downloadReport", "endRegisterSession", "financeWebService", "generateFullReceiptId", "getFirstReceiptDate", "getKasseSettings", "getPrintJob", "getReceipt", "hobexPayApi", "hobexRefundApi", "listMyArticleGroups", "listMyArticles", "listMyCashregisters", "listMyPrinters", "listMyReceipts", "listMyTipRecipients", "listRegisterUsersForDevice", "pairRegisterDevice", "registerPinLogin", "registerUserLogin", "renewRegisterSession", "setMyKasseSettings", "setMyRegisterDeviceSettings", "stripeCaptureIntent", "unpairRegisterDevice"];
16
+ export declare const AUFRUFE: readonly ["cancelReceipt", "createPaymentLinkStripe", "createPrintJob", "createReceipt", "downloadDailyReport", "downloadReport", "endRegisterSession", "financeWebService", "generateFullReceiptId", "getFirstReceiptDate", "getKasseSettings", "getPrintJob", "getReceipt", "hobexPayApi", "hobexRefundApi", "listMyArticleGroups", "listMyArticles", "listMyCashregisters", "listMyPrinters", "listMyReceipts", "listMyTipRecipients", "listRegisterUsersForDevice", "listRegisterSessionsForDevice", "pairRegisterDevice", "registerPinLogin", "registerUserLogin", "renewRegisterSession", "setMyKasseSettings", "setMyRegisterDeviceSettings", "stripeCaptureIntent", "unpairRegisterDevice"];
17
17
  export type Aufruf = typeof AUFRUFE[number];
18
18
  /** Wie [KasseneckTransport], nur mit bekanntem Aufrufnamen. Nicht exportiert nach aussen. */
19
19
  export type InternerTransport = <T = unknown>(functionName: Aufruf, params?: Record<string, unknown>, extraBodyFields?: TransportBodyFields, secretParams?: readonly string[]) => Promise<T>;
@@ -21,6 +21,7 @@ export const AUFRUFE = [
21
21
  'listMyReceipts',
22
22
  'listMyTipRecipients',
23
23
  'listRegisterUsersForDevice',
24
+ 'listRegisterSessionsForDevice',
24
25
  'pairRegisterDevice',
25
26
  'registerPinLogin',
26
27
  'registerUserLogin',
@@ -76,7 +76,13 @@ export declare class KasseneckApiError extends Error {
76
76
  readonly functionName: string;
77
77
  /** Meldung des Backends, unveraendert (`message` aus der Huelle). */
78
78
  readonly serverMessage: string;
79
- constructor(functionName: string, serverMessage: string);
79
+ /**
80
+ * Stabiler Fehlercode des Backends (`code` aus der Huelle), wenn der
81
+ * Endpunkt einen legt — heute `cancelReceipt` (siehe CANCELLATION_ERROR_CODES).
82
+ * Daran entscheiden, nie an [serverMessage]: der Text darf sich aendern.
83
+ */
84
+ readonly code: string | undefined;
85
+ constructor(functionName: string, serverMessage: string, code?: string);
80
86
  }
81
87
  /** Warum die Antwort keine verwertbare Huelle war. */
82
88
  export type HttpFailureReason = 'server-error' | 'empty-body' | 'not-json' | 'missing-status';
@@ -92,10 +92,17 @@ export class KasseneckApiError extends Error {
92
92
  functionName;
93
93
  /** Meldung des Backends, unveraendert (`message` aus der Huelle). */
94
94
  serverMessage;
95
- constructor(functionName, serverMessage) {
95
+ /**
96
+ * Stabiler Fehlercode des Backends (`code` aus der Huelle), wenn der
97
+ * Endpunkt einen legt — heute `cancelReceipt` (siehe CANCELLATION_ERROR_CODES).
98
+ * Daran entscheiden, nie an [serverMessage]: der Text darf sich aendern.
99
+ */
100
+ code;
101
+ constructor(functionName, serverMessage, code) {
96
102
  super(`${functionName} fehlgeschlagen: ${serverMessage}`);
97
103
  this.functionName = functionName;
98
104
  this.serverMessage = serverMessage;
105
+ this.code = code;
99
106
  }
100
107
  }
101
108
  const GRUND_TEXT = {
@@ -54,6 +54,22 @@ export interface TipOptions {
54
54
  paymentMethod?: KeckPaymentMethod | KeckPaymentMethodKey;
55
55
  /** Empfaenger; ohne Angabe der angemeldete Kassen-Benutzer. Summe der cents = cents. */
56
56
  recipients?: TipRecipientShare[];
57
+ /**
58
+ * Hat der Empfaenger das Geld schon? `true` = ja (Bargeld mitgenommen,
59
+ * Kartentrinkgeld sofort aus der Lade ausgezahlt), `false` = der Betrieb
60
+ * behaelt es und schuldet es.
61
+ *
62
+ * Entscheidend ist NICHT die Zahlart, sondern der Besitz -- § 2j Abs 2 AVRAG
63
+ * kennt beide Faelle. Ohne Angabe gilt die Voreinstellung des Betriebs (bar:
64
+ * schon erhalten, bargeldlos: einbehalten); diese Angabe braucht es nur fuer
65
+ * die Ausnahme.
66
+ *
67
+ * NUR mit dem Recht `tipAssign` bei persoenlicher Anmeldung: das Merkmal
68
+ * entscheidet, ob der Betrieb Geld schuldet, und gewoehnliches Personal soll
69
+ * das nicht am Geraet umstellen koennen. Ueber einen Geraete-API-Schluessel
70
+ * (ohne angemeldeten Kassen-Benutzer) gilt die Einschraenkung nicht.
71
+ */
72
+ sofortErhalten?: boolean;
57
73
  }
58
74
  export interface TipRecipientShare {
59
75
  registerUserId: string;
@@ -385,6 +385,16 @@ function gepruefterTip(tip) {
385
385
  const nutzlast = { cents: tip.cents };
386
386
  if (tip.paymentMethod != null)
387
387
  nutzlast['paymentMethod'] = gepruefteZahlungsart(tip.paymentMethod);
388
+ // Nur mitschicken, wenn gesetzt: fehlt das Feld, entscheidet die
389
+ // Voreinstellung des Betriebs. Ein `false` waere dort eine Aussage, kein
390
+ // Weglassen. Der Wortlaut des Fehlers ist der des Backends
391
+ // (tip-core.normalizeTip) -- wer ihn hier sieht, sieht denselben Satz.
392
+ if (tip.sofortErhalten != null) {
393
+ if (typeof tip.sofortErhalten !== 'boolean') {
394
+ throw eingabefehler('Trinkgeld: sofortErhalten muss true oder false sein.');
395
+ }
396
+ nutzlast['sofortErhalten'] = tip.sofortErhalten;
397
+ }
388
398
  if (tip.recipients != null) {
389
399
  if (!Array.isArray(tip.recipients) || tip.recipients.length === 0) {
390
400
  throw eingabefehler('Trinkgeld: recipients darf nicht leer sein.');
@@ -176,7 +176,7 @@ function jsonAuswerten(text, functionName, statusCode, inhaltstyp) {
176
176
  }
177
177
  // Alles, was nicht ausdruecklich Erfolg ist, gilt als fachlicher Fehler —
178
178
  // ein unbekannter Statuswert darf nie stillschweigend als Erfolg durchgehen.
179
- throw fachfehler(functionName, huelle.message);
179
+ throw fachfehler(functionName, huelle.message, huelle.code);
180
180
  }
181
181
  /**
182
182
  * Auswertung des Binaerwegs. Der Kern der Zusage: **ein Aufrufer bekommt nie
@@ -222,7 +222,7 @@ function pdfAuswerten(bytes, functionName, statusCode, inhaltstyp) {
222
222
  }
223
223
  // Derselbe fachliche Fehler wie auf dem JSON-Weg — fuer den Aufrufer macht
224
224
  // es keinen Unterschied, ob er ein PDF oder eine Nutzlast erwartet hat.
225
- throw fachfehler(functionName, huelle.message);
225
+ throw fachfehler(functionName, huelle.message, huelle.code);
226
226
  }
227
227
  /** `%PDF` am Anfang — die Kennung jeder PDF-Datei. */
228
228
  function istPdf(bytes) {
@@ -235,9 +235,10 @@ function alsHuelle(wert) {
235
235
  }
236
236
  return wert;
237
237
  }
238
- function fachfehler(functionName, message) {
238
+ function fachfehler(functionName, message, code) {
239
239
  const meldung = typeof message === 'string' && message.trim() ? message : 'Unbekannter Fehler';
240
- return new KasseneckApiError(functionName, meldung);
240
+ // Nur ein Text zaehlt als Code -- alles andere waere ein geratener Vertrag.
241
+ return new KasseneckApiError(functionName, meldung, typeof code === 'string' && code ? code : undefined);
241
242
  }
242
243
  /**
243
244
  * Nutzlast aus Auth- und Aufruferparametern. Auth-Parameter bilden die
@@ -1,5 +1,5 @@
1
1
  export { ReceiptType, type ReceiptTypeKey, VatRate, type VatRateKey, KeckPaymentMethod, type KeckPaymentMethodKey, CreditCardProvider, type CreditCardProviderKey, VoucherType, type VoucherTypeKey, VoucherAction, type VoucherActionKey, StripeLinkMode, type StripeLinkModeKey, } from './enums/index.js';
2
- export { type ReceiptItem, type ReceiptItemPayload, type ReceiptItemPayloadRead, toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, type TipRecipient, isTipItem, type Voucher, type VoucherPayload, toVoucherPayload, fromVoucherPayload, voucherIsValid, type Receipt, type ReceiptPayload, type ReceiptPayloadRead, toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, type CancellationOf, type CancellationItem, type Cancellation, type CancellationReason, CANCELLATION_REASONS, CANCELLATION_RESERVATION_MS, isCancellationReason, remainingQuantities, type ReceiptCompany, type ReceiptCompanyPayload, fromReceiptCompanyPayload, receiptCompanyTaxInfo, type Cashregister, type CashregisterOnboarding, type CashregisterPayload, type CashregisterOnboardingPayload, fromCashregisterPayload, type ReceiptSummary, type ReceiptSummaryPayload, fromReceiptSummaryPayload, type ZeroKind, ZERO_KINDS, istZeroKind, type ReportMonth, reportMonthFromDate, previousReportMonth, nextReportMonth, reportMonthKey, reportMonthReadable, type StripeUrlSession, type StripeUrlSessionPayload, toStripeUrlSessionPayload, fromStripeUrlSessionPayload, type HobexReceipt, type HobexReceiptPayload, toHobexReceiptPayload, fromHobexReceiptPayload, hobexReceiptToCardPaymentData, hobexReceiptNeedsSignature, } from './models/index.js';
2
+ export { type ReceiptItem, type ReceiptItemPayload, type ReceiptItemPayloadRead, toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, type TipRecipient, isTipItem, type Voucher, type VoucherPayload, toVoucherPayload, fromVoucherPayload, voucherIsValid, type Receipt, type ReceiptPayload, type ReceiptPayloadRead, toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, type CancellationOf, type CancellationItem, type Cancellation, type CancellationReason, type CancellationErrorCode, CANCELLATION_REASONS, CANCELLATION_ERROR_CODES, CANCELLATION_RESERVATION_MS, isCancellationReason, isCancellationErrorCode, remainingQuantities, type ReceiptCompany, type ReceiptCompanyPayload, fromReceiptCompanyPayload, receiptCompanyTaxInfo, type Cashregister, type CashregisterOnboarding, type CashregisterPayload, type CashregisterOnboardingPayload, fromCashregisterPayload, type ReceiptSummary, type ReceiptSummaryPayload, fromReceiptSummaryPayload, type ZeroKind, ZERO_KINDS, istZeroKind, type ReportMonth, reportMonthFromDate, previousReportMonth, nextReportMonth, reportMonthKey, reportMonthReadable, type StripeUrlSession, type StripeUrlSessionPayload, toStripeUrlSessionPayload, fromStripeUrlSessionPayload, type HobexReceipt, type HobexReceiptPayload, toHobexReceiptPayload, fromHobexReceiptPayload, hobexReceiptToCardPaymentData, hobexReceiptNeedsSignature, } from './models/index.js';
3
3
  export { type AuthCredentials, type KasseneckAuth, type ApiKeyAuthOptions, type RegisterUserAuthOptions, apiKeyAuth, registerUserAuth, DEFAULT_BASE_URL, DEFAULT_TIMEOUT_MS, type HttpResponseLike, type HttpRequestInit, type FetchLike, type TransportOptions, type TransportBodyFields, type KasseneckTransport, type KasseneckBinaryTransport, AUFRUFE, type Aufruf, createTransport, createBinaryTransport, KasseneckApiError, KasseneckAuthError, KasseneckHttpError, KasseneckNetworkError, KasseneckValidationError, type ValidationScope, type KasseneckError, type HttpFailureReason, type CauseDigest, isKasseneckApiError, isKasseneckAuthError, isKasseneckHttpError, isKasseneckNetworkError, isKasseneckValidationError, type ReceiptCommonOptions, type SellReceiptOptions, type TipOptions, type TipRecipientShare, type CancelReceiptOptions, type CancelReceiptResult, type CreateCancelReceiptOptions, type ReceiptWithCompany, sellReceipt, sellReceiptWithCompany, cancelReceipt, createCancelReceipt, zeroReceipt, getReceipt, getReceiptWithCompany, generateFullReceiptId, getFirstReceiptDate, listMyReceipts, type ListMyReceiptsOptions, type ReceiptList, type ReceiptListStats, listMyCashregisters, checkVoucherCombinationError, downloadDailyReport, downloadMonthlyReport, type CashboxStatus, type SignatureStatus, getCashboxStatus, getSignatureStatus, type KasseneckApi, createKasseneckApi, } from './client/index.js';
4
4
  export { parseServerTimeStamp, toViennaWallClock, type ViennaWallClock } from './vienna-time.js';
5
5
  export { type KasseSettingsBetrieb, type KasseSettingsGeraet, type KasseSettings, type ArticleGroup, type KasseArtikel, KASSE_BETRIEB_STANDARD, KASSE_GERAET_STANDARD, mergeKasseSettings, fromArticleGroupPayload, fromKasseArtikelPayload, listMyArticleGroups, listMyArticles, getKasseSettings, setMyKasseSettings, setMyRegisterDeviceSettings, verteileRabatt, cancelScopeOf, receiptsScopeOf, } from './kasse/index.js';
package/dist/esm/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { ReceiptType, VatRate, KeckPaymentMethod, CreditCardProvider, VoucherType, VoucherAction, StripeLinkMode, } from './enums/index.js';
2
- export { toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, isTipItem, toVoucherPayload, fromVoucherPayload, voucherIsValid, toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, CANCELLATION_REASONS, CANCELLATION_RESERVATION_MS, isCancellationReason, remainingQuantities, fromReceiptCompanyPayload, receiptCompanyTaxInfo, fromCashregisterPayload, fromReceiptSummaryPayload, ZERO_KINDS, istZeroKind, reportMonthFromDate, previousReportMonth, nextReportMonth, reportMonthKey, reportMonthReadable, toStripeUrlSessionPayload, fromStripeUrlSessionPayload, toHobexReceiptPayload, fromHobexReceiptPayload, hobexReceiptToCardPaymentData, hobexReceiptNeedsSignature, } from './models/index.js';
2
+ export { toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, isTipItem, toVoucherPayload, fromVoucherPayload, voucherIsValid, toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, CANCELLATION_REASONS, CANCELLATION_ERROR_CODES, CANCELLATION_RESERVATION_MS, isCancellationReason, isCancellationErrorCode, remainingQuantities, fromReceiptCompanyPayload, receiptCompanyTaxInfo, fromCashregisterPayload, fromReceiptSummaryPayload, ZERO_KINDS, istZeroKind, reportMonthFromDate, previousReportMonth, nextReportMonth, reportMonthKey, reportMonthReadable, toStripeUrlSessionPayload, fromStripeUrlSessionPayload, toHobexReceiptPayload, fromHobexReceiptPayload, hobexReceiptToCardPaymentData, hobexReceiptNeedsSignature, } from './models/index.js';
3
3
  export { apiKeyAuth, registerUserAuth, DEFAULT_BASE_URL, DEFAULT_TIMEOUT_MS, AUFRUFE, createTransport, createBinaryTransport, KasseneckApiError, KasseneckAuthError, KasseneckHttpError, KasseneckNetworkError, KasseneckValidationError, isKasseneckApiError, isKasseneckAuthError, isKasseneckHttpError, isKasseneckNetworkError, isKasseneckValidationError, sellReceipt, sellReceiptWithCompany, cancelReceipt, createCancelReceipt, zeroReceipt, getReceipt, getReceiptWithCompany, generateFullReceiptId, getFirstReceiptDate, listMyReceipts, listMyCashregisters, checkVoucherCombinationError, downloadDailyReport, downloadMonthlyReport, getCashboxStatus, getSignatureStatus, createKasseneckApi, } from './client/index.js';
4
4
  export { parseServerTimeStamp, toViennaWallClock } from './vienna-time.js';
5
5
  export { KASSE_BETRIEB_STANDARD, KASSE_GERAET_STANDARD, mergeKasseSettings, fromArticleGroupPayload, fromKasseArtikelPayload, listMyArticleGroups, listMyArticles, getKasseSettings, setMyKasseSettings, setMyRegisterDeviceSettings, verteileRabatt, cancelScopeOf, receiptsScopeOf, } from './kasse/index.js';
@@ -18,6 +18,19 @@ export declare const CANCELLATION_REASONS: Readonly<{
18
18
  }>;
19
19
  export type CancellationReason = keyof typeof CANCELLATION_REASONS;
20
20
  export declare function isCancellationReason(value: unknown): value is CancellationReason;
21
+ /**
22
+ * Stabile Fehlercodes von `cancelReceipt` — Zwilling von
23
+ * `functions/storno-core.js` STORNO_FEHLERCODES. Das Backend legt sie bei
24
+ * jedem fachlichen Fehler als `code` neben die Meldung; das Paket reicht sie
25
+ * als [KasseneckApiError.code] durch. **Entscheide am Code, nie am Text** —
26
+ * die deutsche Meldung darf sich aendern, der Code nicht.
27
+ *
28
+ * Nur Auth-/Parameterfehler (Sitzung abgelaufen, Pflichtfeld fehlt) kommen
29
+ * ohne Code; dort bleibt `code` undefined.
30
+ */
31
+ export declare const CANCELLATION_ERROR_CODES: readonly ["beleg_nicht_gefunden", "belegart_nicht_stornierbar", "trainingsbeleg", "bereits_storniert", "position_ungueltig", "menge_ueber_rest", "grund_unbekannt", "anmerkung_zu_lang", "items_ungueltig", "kasse_nicht_zugewiesen", "keine_berechtigung", "nur_eigene_belege", "kasse_unvollstaendig", "storno_fehlgeschlagen"];
32
+ export type CancellationErrorCode = (typeof CANCELLATION_ERROR_CODES)[number];
33
+ export declare function isCancellationErrorCode(value: unknown): value is CancellationErrorCode;
21
34
  /** Bezug eines Storno-Belegs auf sein Original. */
22
35
  export interface CancellationOf {
23
36
  receiptId: string;
@@ -40,6 +53,15 @@ export interface Cancellation {
40
53
  by: string | null;
41
54
  note: string | null;
42
55
  items: CancellationItem[];
56
+ /**
57
+ * Rabattgutschein-Ausgleich, den DIESER Storno gewaehrt hat — Cent je
58
+ * Steuertopf des Backends (`amountRateStandard`, `amountRateReduced1`, …).
59
+ * Ein Rabattgutschein ist am Original schon in den Umsatz eingerechnet;
60
+ * jeder Storno nimmt ihn anteilig der stornierten Menge zurueck (bei 3 Stueck
61
+ * a 10 EUR mit 6 EUR Rabatt: 2,00 je Stueck). Fehlt das Feld, hat der Eintrag
62
+ * nichts gewaehrt (Altbestand vor dieser Regel) — der naechste Storno holt nach.
63
+ */
64
+ promoAdjustmentCents?: Record<string, number>;
43
65
  }
44
66
  /** Ab wann eine liegengebliebene Reservierung nicht mehr zaehlt (wie im Backend). */
45
67
  export declare const CANCELLATION_RESERVATION_MS = 120000;
@@ -18,6 +18,35 @@ export const CANCELLATION_REASONS = Object.freeze({
18
18
  export function isCancellationReason(value) {
19
19
  return typeof value === 'string' && Object.prototype.hasOwnProperty.call(CANCELLATION_REASONS, value);
20
20
  }
21
+ /**
22
+ * Stabile Fehlercodes von `cancelReceipt` — Zwilling von
23
+ * `functions/storno-core.js` STORNO_FEHLERCODES. Das Backend legt sie bei
24
+ * jedem fachlichen Fehler als `code` neben die Meldung; das Paket reicht sie
25
+ * als [KasseneckApiError.code] durch. **Entscheide am Code, nie am Text** —
26
+ * die deutsche Meldung darf sich aendern, der Code nicht.
27
+ *
28
+ * Nur Auth-/Parameterfehler (Sitzung abgelaufen, Pflichtfeld fehlt) kommen
29
+ * ohne Code; dort bleibt `code` undefined.
30
+ */
31
+ export const CANCELLATION_ERROR_CODES = [
32
+ 'beleg_nicht_gefunden', // Original fehlt oder gehoert nicht zu dieser Kasse
33
+ 'belegart_nicht_stornierbar', // Original ist selbst Storno-, Null- oder Startbeleg
34
+ 'trainingsbeleg', // Trainingsbelege werden nicht storniert
35
+ 'bereits_storniert', // keine Restmenge mehr (auch beim positionslosen Beleg)
36
+ 'position_ungueltig', // Index unbekannt oder doppelt
37
+ 'menge_ueber_rest', // Menge nicht ganzzahlig >= 1 oder groesser als der Rest
38
+ 'grund_unbekannt', // reason fehlt oder nicht im Katalog
39
+ 'anmerkung_zu_lang', // note laenger als 200 Zeichen
40
+ 'items_ungueltig', // items ist keine Liste
41
+ 'kasse_nicht_zugewiesen', // Register-Benutzer darf diese Kasse nicht
42
+ 'keine_berechtigung', // Register-Benutzer ohne Storno-Recht
43
+ 'nur_eigene_belege', // Recht "eigene", fremder Beleg
44
+ 'kasse_unvollstaendig', // api_key/token fehlen am Konto bzw. an der Kasse
45
+ 'storno_fehlgeschlagen', // der Storno-Beleg selbst wurde abgelehnt (z. B. Signatur)
46
+ ];
47
+ export function isCancellationErrorCode(value) {
48
+ return typeof value === 'string' && CANCELLATION_ERROR_CODES.includes(value);
49
+ }
21
50
  /** Ab wann eine liegengebliebene Reservierung nicht mehr zaehlt (wie im Backend). */
22
51
  export const CANCELLATION_RESERVATION_MS = 120_000;
23
52
  /**
@@ -1,5 +1,5 @@
1
1
  import { CreditCardProvider } from '../enums/index.js';
2
- import { centsToEuro } from '../money.js';
2
+ import { centsToEuro, euroToCents } from '../money.js';
3
3
  export function toHobexReceiptPayload(beleg) {
4
4
  return {
5
5
  transactionId: beleg.transactionId,
@@ -38,8 +38,15 @@ export function fromHobexReceiptPayload(payload) {
38
38
  responseCode: payload.responseCode,
39
39
  transactionType: payload.transactionType,
40
40
  currency: payload.currency,
41
- amountCents: Math.round((payload.amount ?? 0) * 100),
42
- tipCents: Math.round((payload.tip ?? 0) * 100),
41
+ // Euro->Cent ueber die eine gehaertete Stelle im Paket ([euroToCents],
42
+ // money.ts) statt einer eigenen `Math.round(x * 100)` -- die Hobex-Antwort
43
+ // ist eine fremde Antwort und wird hier nur auf transactionId/
44
+ // transactionDate geprueft (siehe payments/hobex.ts, belegAusNutzlast);
45
+ // amount/tip bleiben ungeprueft. Ein nicht-numerischer oder
46
+ // nicht-endlicher Wert ergab bis zu dieser Fassung `NaN` -- lautlos, ohne
47
+ // dass irgendwo geworfen wurde. [euroToCents] liefert dafuer `0`.
48
+ amountCents: euroToCents(payload.amount),
49
+ tipCents: euroToCents(payload.tip),
43
50
  cvm: String(payload.cvm),
44
51
  // Die Cloud-API-Nutzlast traegt keinen eigenen Provider-Schluessel — der
45
52
  // Provider ergibt sich aus dem Transportweg (Cloud vs. HPS), nicht aus
@@ -1,7 +1,7 @@
1
1
  export { type ReceiptItem, type ReceiptItemPayload, type ReceiptItemPayloadRead, toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, type TipRecipient, isTipItem, isDiscountItem, } from './receipt-item.js';
2
2
  export { type Voucher, type VoucherPayload, toVoucherPayload, fromVoucherPayload, voucherIsValid } from './voucher.js';
3
3
  export { type Receipt, type ReceiptPayload, type ReceiptPayloadRead, toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, } from './receipt.js';
4
- export { type CancellationOf, type CancellationItem, type Cancellation, type CancellationReason, CANCELLATION_REASONS, CANCELLATION_RESERVATION_MS, isCancellationReason, remainingQuantities, } from './cancellation.js';
4
+ export { type CancellationOf, type CancellationItem, type Cancellation, type CancellationReason, type CancellationErrorCode, CANCELLATION_REASONS, CANCELLATION_ERROR_CODES, CANCELLATION_RESERVATION_MS, isCancellationReason, isCancellationErrorCode, remainingQuantities, } from './cancellation.js';
5
5
  export { type ReceiptCompany, type ReceiptCompanyPayload, fromReceiptCompanyPayload, receiptCompanyTaxInfo, } from './receipt-company.js';
6
6
  export { type Cashregister, type CashregisterOnboarding, type CashregisterPayload, type CashregisterOnboardingPayload, fromCashregisterPayload, } from './cashregister.js';
7
7
  export { type ReceiptSummary, type ReceiptSummaryPayload, fromReceiptSummaryPayload, type ZeroKind, ZERO_KINDS, istZeroKind } from './receipt-summary.js';
@@ -1,7 +1,7 @@
1
1
  export { toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, isTipItem, isDiscountItem, } from './receipt-item.js';
2
2
  export { toVoucherPayload, fromVoucherPayload, voucherIsValid } from './voucher.js';
3
3
  export { toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, } from './receipt.js';
4
- export { CANCELLATION_REASONS, CANCELLATION_RESERVATION_MS, isCancellationReason, remainingQuantities, } from './cancellation.js';
4
+ export { CANCELLATION_REASONS, CANCELLATION_ERROR_CODES, CANCELLATION_RESERVATION_MS, isCancellationReason, isCancellationErrorCode, remainingQuantities, } from './cancellation.js';
5
5
  export { fromReceiptCompanyPayload, receiptCompanyTaxInfo, } from './receipt-company.js';
6
6
  export { fromCashregisterPayload, } from './cashregister.js';
7
7
  export { fromReceiptSummaryPayload, ZERO_KINDS, istZeroKind } from './receipt-summary.js';
@@ -84,6 +84,9 @@ function leseStorno(eintrag) {
84
84
  by: eintrag.by ?? null,
85
85
  note: eintrag.note ?? null,
86
86
  items: (eintrag.items ?? []).map((p) => ({ index: Number(p.index), quantity: Number(p.quantity) })),
87
+ ...(eintrag.promoAdjustmentCents && typeof eintrag.promoAdjustmentCents === 'object'
88
+ ? { promoAdjustmentCents: Object.fromEntries(Object.entries(eintrag.promoAdjustmentCents).map(([k, v]) => [k, Number(v)])) }
89
+ : {}),
87
90
  };
88
91
  }
89
92
  /**
@@ -1,4 +1,5 @@
1
1
  import { VoucherAction, VoucherType } from '../enums/index.js';
2
+ import { euroToCents } from '../money.js';
2
3
  import { readEnumKey, requireEnumKey } from './enum-payload.js';
3
4
  export function toVoucherPayload(voucher) {
4
5
  return {
@@ -30,16 +31,31 @@ export function voucherIsValid(voucher) {
30
31
  if (voucher.type === VoucherType.promo && voucher.valueCents == null) {
31
32
  return false;
32
33
  }
33
- if (voucher.valueCents != null && voucher.valueCents <= 0) {
34
+ // `!Number.isFinite` VOR den Vergleich, nicht dahinter: `NaN <= 0` und
35
+ // `Infinity <= 0` sind beide `false`, ein Vergleich mit einem
36
+ // nicht-endlichen Wert ist also IMMER falsch und liesse ihn hier
37
+ // durchrutschen. Gemessen (28.08.2026): ein Gutschein mit `valueCents: NaN`
38
+ // (aus einem kaputten `value` in der Nutzlast, siehe [fromVoucherPayload])
39
+ // oder `valueCents: Infinity` galt bis zu dieser Fassung als GUELTIG und
40
+ // waere still in die Belegsummen gewandert. Diese Pruefung greift
41
+ // unabhaengig davon, ob `valueCents` ueber [fromVoucherPayload] oder direkt
42
+ // gesetzt wurde -- die gehaertete Umrechnung dort allein reicht nicht.
43
+ if (voucher.valueCents != null && (!Number.isFinite(voucher.valueCents) || voucher.valueCents <= 0)) {
34
44
  return false;
35
45
  }
36
46
  return true;
37
47
  }
38
48
  export function fromVoucherPayload(payload) {
49
+ // Euro->Cent ueber die eine gehaertete Stelle im Paket ([euroToCents],
50
+ // money.ts) statt einer eigenen `Math.round(payload.value * 100)` --
51
+ // [euroToCents] prueft `Number.isFinite` und liefert fuer einen
52
+ // nicht-numerischen oder nicht-endlichen Wert `0` statt eines lautlosen
53
+ // `NaN`. Siehe auch [voucherIsValid]: `0` faellt dort ueber die
54
+ // "<= 0"-Pruefung, ein `NaN` waere durchgerutscht.
39
55
  const valueCents = payload.valueCents != null
40
56
  ? payload.valueCents
41
57
  : payload.value != null
42
- ? Math.round(payload.value * 100)
58
+ ? euroToCents(payload.value)
43
59
  : undefined;
44
60
  return {
45
61
  name: payload.name ?? undefined,
@@ -0,0 +1,104 @@
1
+ import { type HpsTransactionResponse } from './transaction-response.js';
2
+ /**
3
+ * Client fuer die Terminal-Endpunkte von **Kasseneck Connect** — dem lokalen
4
+ * Geraete-Agenten, der ueber HTTP erreichbar ist und mit dem hobex-HPS
5
+ * spricht (`kasseneck-connect/lib/src/api/routes_terminal.dart`). Connect
6
+ * reicht die Antwort des Terminals ROH durch und ordnet nichts ein; die
7
+ * Einordnung passiert in `transaction-response.ts` und `payments.ts`.
8
+ *
9
+ * **Kein direkter Terminal-Kontakt.** Ein Browser hat keine rohen
10
+ * TCP-Sockets — das ist der Unterschied zum Dart-Zwilling `HpsClient`, der
11
+ * das HPS-Terminal direkt anspricht. Dieser Client spricht ausschliesslich
12
+ * mit Connect, niemals mit dem Terminal selbst.
13
+ *
14
+ * **`payment`/`status`/`abort`/`refund`/`cancel`.** Seit
15
+ * `kasseneck-connect` Commit `1c8a003` traegt Connect auch die Gutschrift
16
+ * (`POST /v1/terminal/refund`) und die Aufhebung (`POST /v1/terminal/cancel`)
17
+ * — beide erben denselben Fehlerpfad wie `payment` (`error.detail.terminalHttpStatus`,
18
+ * siehe `HpsConnectTerminalError.isTerminalBusy`).
19
+ */
20
+ /** Adresse des Ziel-Terminals — bei jedem Aufruf mitgegeben, Connect selbst ist zustandslos. */
21
+ export interface HpsConnectTarget {
22
+ /** IP oder Hostname des Terminals im Kassen-Netz. */
23
+ host: string;
24
+ /** Vorgabe: `8080` (hobex-HPS-Standardport), siehe Connect. */
25
+ port?: number;
26
+ /** Terminal-ID, wie am Geraet angezeigt (fuehrende Nullen werden toleriert). */
27
+ tid: string;
28
+ }
29
+ export interface HpsConnectPaymentOptions extends HpsConnectTarget {
30
+ /** Kennung dieser Zahlung — MUSS vor diesem Aufruf feststehen, siehe `transaction-id.ts`. */
31
+ transactionId: string;
32
+ /** Zu belastender Betrag in **Cent** (ohne Trinkgeld). */
33
+ amountCents: number;
34
+ /** Trinkgeld in **Cent**; das Terminal belastet Betrag + Trinkgeld in einem. */
35
+ tipCents?: number;
36
+ reference?: string;
37
+ currency?: string;
38
+ language?: string;
39
+ }
40
+ export interface HpsConnectTransactionOptions extends HpsConnectTarget {
41
+ transactionId: string;
42
+ }
43
+ export interface HpsConnectRefundOptions extends HpsConnectTarget {
44
+ /** Kennung der Gutschrift SELBST — eine NEUE, nicht die der erstatteten Zahlung. */
45
+ transactionId: string;
46
+ /** Kennung der erstatteten Zahlung. Verlangt Connect zwingend, siehe `_handleRefund`. */
47
+ originalTransactionId: string;
48
+ /** Zu erstattender Betrag in **Cent**. */
49
+ amountCents: number;
50
+ reference?: string;
51
+ currency?: string;
52
+ language?: string;
53
+ }
54
+ export interface HpsConnectCancelOptions extends HpsConnectTarget {
55
+ /** Kennung der URSPRUENGLICHEN Zahlung — keine neue. */
56
+ transactionId: string;
57
+ /** Pflicht: das Terminal weist einen Void ohne Betrag mit `400 Missing amount` ab. */
58
+ amountCents: number;
59
+ currency?: string;
60
+ language?: string;
61
+ }
62
+ /** Austauschbare `fetch`-Umsetzung, minimal genug fuer eine Attrappe im Test. */
63
+ export interface HpsConnectFetchResponse {
64
+ status: number;
65
+ text(): Promise<string>;
66
+ }
67
+ export type HpsConnectFetch = (url: string, init: {
68
+ method: string;
69
+ headers: Record<string, string>;
70
+ body: string;
71
+ signal: AbortSignal;
72
+ }) => Promise<HpsConnectFetchResponse>;
73
+ export interface HpsConnectClientOptions {
74
+ /** Basisadresse des Agenten. Vorgabe `http://127.0.0.1:27182` (README `kasseneck-connect`). */
75
+ baseUrl?: string;
76
+ /** Kopplungstoken, geht als `Authorization: Bearer <token>` mit. */
77
+ token: string;
78
+ /**
79
+ * Zeitlimit fuer `status`/`abort`/`diagnosis` in ms. Vorgabe 30 s — deutlich
80
+ * ueber Connects eigenem `hpsKurzTimeout` (15 s), damit dieser Client nicht
81
+ * VOR Connect selbst aufgibt und dessen Fehlermeldung verschluckt.
82
+ */
83
+ shortTimeoutMs?: number;
84
+ /**
85
+ * Zeitlimit fuer `payment` in ms. Vorgabe 4,5 Minuten — ueber Connects
86
+ * eigenem `hpsZahlungTimeout` (4 Minuten): eine Zahlung blockiert, bis der
87
+ * Karteninhaber fertig ist, das darf nicht vorzeitig aufgegeben werden.
88
+ */
89
+ paymentTimeoutMs?: number;
90
+ /** Eigene `fetch`-Umsetzung (Tests). Vorgabe: globales `fetch`. */
91
+ fetch?: HpsConnectFetch;
92
+ }
93
+ export interface HpsConnectClient {
94
+ payment(options: HpsConnectPaymentOptions): Promise<HpsTransactionResponse>;
95
+ status(options: HpsConnectTransactionOptions): Promise<HpsTransactionResponse>;
96
+ abort(options: HpsConnectTransactionOptions): Promise<HpsTransactionResponse>;
97
+ /** Gutschrift — ein Kartenfluss wie [payment], deshalb dieselbe lange Frist. */
98
+ refund(options: HpsConnectRefundOptions): Promise<HpsTransactionResponse>;
99
+ /** Aufhebung (Storno/Void) — kurzer Aufruf, keine Karteninteraktion. */
100
+ cancel(options: HpsConnectCancelOptions): Promise<HpsTransactionResponse>;
101
+ /** Erreichbarkeits-/Firmware-Check, folgenlos. Rohe Nutzlast, Connect ordnet nichts ein. */
102
+ diagnosis(options: HpsConnectTarget): Promise<Record<string, unknown>>;
103
+ }
104
+ export declare function createHpsConnectClient(options: HpsConnectClientOptions): HpsConnectClient;