@kreiseck/kasseneck-api 0.6.45 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +154 -0
- package/README.md +158 -2
- package/dist/cjs/client/aufrufe.d.ts +1 -1
- package/dist/cjs/client/aufrufe.js +20 -0
- package/dist/cjs/client/errors.d.ts +31 -1
- package/dist/cjs/client/errors.js +98 -1
- package/dist/cjs/client/transport.js +7 -7
- package/dist/cjs/kasse/index.d.ts +1 -0
- package/dist/cjs/kasse/index.js +3 -0
- package/dist/cjs/kasse/trinkgeld.d.ts +10 -0
- package/dist/cjs/kasse/trinkgeld.js +27 -0
- package/dist/cjs/partner/ablauf.d.ts +47 -0
- package/dist/cjs/partner/ablauf.js +99 -0
- package/dist/cjs/partner/api.d.ts +68 -0
- package/dist/cjs/partner/api.js +44 -0
- package/dist/cjs/partner/auth.d.ts +33 -0
- package/dist/cjs/partner/auth.js +52 -0
- package/dist/cjs/partner/betrieb.d.ts +48 -0
- package/dist/cjs/partner/betrieb.js +136 -0
- package/dist/cjs/partner/endpunkte.d.ts +142 -0
- package/dist/cjs/partner/endpunkte.js +488 -0
- package/dist/cjs/partner/fehler.d.ts +72 -0
- package/dist/cjs/partner/fehler.js +199 -0
- package/dist/cjs/partner/index.d.ts +28 -0
- package/dist/cjs/partner/index.js +84 -0
- package/dist/cjs/partner/secret.d.ts +66 -0
- package/dist/cjs/partner/secret.js +95 -0
- package/dist/cjs/partner/typen.d.ts +399 -0
- package/dist/cjs/partner/typen.js +33 -0
- package/dist/cjs/partner/webhook-signatur.d.ts +95 -0
- package/dist/cjs/partner/webhook-signatur.js +158 -0
- package/dist/cjs/partner/webhooks.d.ts +211 -0
- package/dist/cjs/partner/webhooks.js +269 -0
- package/dist/cjs/register/pairing.d.ts +8 -1
- package/dist/cjs/register/pairing.js +8 -1
- package/dist/esm/client/aufrufe.d.ts +1 -1
- package/dist/esm/client/aufrufe.js +20 -0
- package/dist/esm/client/errors.d.ts +31 -1
- package/dist/esm/client/errors.js +97 -1
- package/dist/esm/client/transport.js +8 -8
- package/dist/esm/kasse/index.d.ts +1 -0
- package/dist/esm/kasse/index.js +1 -0
- package/dist/esm/kasse/trinkgeld.d.ts +10 -0
- package/dist/esm/kasse/trinkgeld.js +24 -0
- package/dist/esm/partner/ablauf.d.ts +47 -0
- package/dist/esm/partner/ablauf.js +95 -0
- package/dist/esm/partner/api.d.ts +68 -0
- package/dist/esm/partner/api.js +41 -0
- package/dist/esm/partner/auth.d.ts +33 -0
- package/dist/esm/partner/auth.js +48 -0
- package/dist/esm/partner/betrieb.d.ts +48 -0
- package/dist/esm/partner/betrieb.js +132 -0
- package/dist/esm/partner/endpunkte.d.ts +142 -0
- package/dist/esm/partner/endpunkte.js +474 -0
- package/dist/esm/partner/fehler.d.ts +72 -0
- package/dist/esm/partner/fehler.js +189 -0
- package/dist/esm/partner/index.d.ts +28 -0
- package/dist/esm/partner/index.js +27 -0
- package/dist/esm/partner/secret.d.ts +66 -0
- package/dist/esm/partner/secret.js +90 -0
- package/dist/esm/partner/typen.d.ts +399 -0
- package/dist/esm/partner/typen.js +30 -0
- package/dist/esm/partner/webhook-signatur.d.ts +95 -0
- package/dist/esm/partner/webhook-signatur.js +153 -0
- package/dist/esm/partner/webhooks.d.ts +211 -0
- package/dist/esm/partner/webhooks.js +257 -0
- package/dist/esm/register/pairing.d.ts +8 -1
- package/dist/esm/register/pairing.js +8 -1
- package/fixtures/oberflaeche.json +132 -3
- package/package.json +13 -1
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Die Fehlercodes der Partner-API und das, was ein Integrator daraufhin tun
|
|
3
|
+
* muss.
|
|
4
|
+
*
|
|
5
|
+
* Das Backend antwortet auf jeden fachlichen Ausgang mit HTTP 200 und legt
|
|
6
|
+
* seine Entscheidung in `data.code` (siehe `docs/api/partner.md` im Backend).
|
|
7
|
+
* Der Transport hebt den Code an `KasseneckApiError.code` — hier steht, was er
|
|
8
|
+
* bedeutet.
|
|
9
|
+
*
|
|
10
|
+
* **Warum die Texte hier stehen und nicht nur im Backend:** die Meldung des
|
|
11
|
+
* Servers sagt, WAS ist. Sie sagt nicht, was der Aufrufer als naechstes tut,
|
|
12
|
+
* und sie kann es auch nicht — dafuer muesste sie seinen Ablauf kennen. Diese
|
|
13
|
+
* Datei ist deshalb kein zweiter Abdruck der Doku, sondern die
|
|
14
|
+
* Handlungsanweisung daneben.
|
|
15
|
+
*
|
|
16
|
+
* **Der Katalog ist vollstaendig.** Die Quelle ist `docs/api/fehlercodes.json`
|
|
17
|
+
* im Backend (Abzug aus `partner-core.FEHLER_KATALOG`); ein Code, den nur eine
|
|
18
|
+
* Seite kennt, ist fuer einen Aufrufer nicht von „gibt es nicht" zu
|
|
19
|
+
* unterscheiden. Deshalb stehen hier BEIDE Flaechen: die der Schnittstelle
|
|
20
|
+
* ([PARTNER_FEHLER_CODES]) und die des Partner-Portals
|
|
21
|
+
* ([PARTNER_PORTAL_FEHLER_CODES]).
|
|
22
|
+
*/
|
|
23
|
+
import { KasseneckApiError } from '../client/errors.js';
|
|
24
|
+
/**
|
|
25
|
+
* Alle Codes, die die **Schnittstelle** kennt. Als Liste und nicht nur als
|
|
26
|
+
* Typ, damit ein Aufrufer sie zur Laufzeit durchgehen kann (Katalogseite,
|
|
27
|
+
* Selbsttest der eigenen Fehlerbehandlung).
|
|
28
|
+
*
|
|
29
|
+
* Reihenfolge und Bestand wie im Abzug des Backends.
|
|
30
|
+
*/
|
|
31
|
+
export const PARTNER_FEHLER_CODES = [
|
|
32
|
+
// Eingabe, Konto und Takt
|
|
33
|
+
'validation',
|
|
34
|
+
'rate_limited',
|
|
35
|
+
'app_not_found',
|
|
36
|
+
'app_not_accepted',
|
|
37
|
+
'kein_partnerbetrieb',
|
|
38
|
+
'live_not_allowed',
|
|
39
|
+
// Betrieb anlegen
|
|
40
|
+
'customer_exists',
|
|
41
|
+
'customer_conflict',
|
|
42
|
+
'customer_limit',
|
|
43
|
+
'zugang_nicht_erlaubt',
|
|
44
|
+
'email_taken',
|
|
45
|
+
'no_email',
|
|
46
|
+
// Signatur
|
|
47
|
+
'fon_missing',
|
|
48
|
+
'signature_pending',
|
|
49
|
+
'request_not_found',
|
|
50
|
+
'signature_missing',
|
|
51
|
+
'signature_unknown',
|
|
52
|
+
'signature_ambiguous',
|
|
53
|
+
'signature_not_ready',
|
|
54
|
+
'signature_limit',
|
|
55
|
+
'signature_failed',
|
|
56
|
+
// Kasse
|
|
57
|
+
'module_inactive',
|
|
58
|
+
'cashregister_limit',
|
|
59
|
+
'cashregister_not_found',
|
|
60
|
+
'activation_failed',
|
|
61
|
+
// Webhooks
|
|
62
|
+
'webhook_limit',
|
|
63
|
+
'webhook_inactive',
|
|
64
|
+
'event_not_subscribed',
|
|
65
|
+
];
|
|
66
|
+
/**
|
|
67
|
+
* Die Codes, die nur im **Partner-Portal** entstehen — beim Pflegen der App,
|
|
68
|
+
* der Schluessel, der Mitglieder und der Signaturkarten.
|
|
69
|
+
*
|
|
70
|
+
* Sie stehen hier, obwohl kein Aufruf dieses Clients sie ausloest: der
|
|
71
|
+
* Fehlerkatalog ist eine Liste, und eine halbe Liste ist schlimmer als keine.
|
|
72
|
+
* Wer eine Katalogseite baut oder eine fremde Antwort einsortiert, findet
|
|
73
|
+
* damit jeden Code des Backends wieder.
|
|
74
|
+
*/
|
|
75
|
+
export const PARTNER_PORTAL_FEHLER_CODES = [
|
|
76
|
+
'app_locked',
|
|
77
|
+
'version_locked',
|
|
78
|
+
'invalid_transition',
|
|
79
|
+
'no_accepted_app',
|
|
80
|
+
'consent',
|
|
81
|
+
'key_limit',
|
|
82
|
+
'last_owner',
|
|
83
|
+
'auth_user_exists',
|
|
84
|
+
'card_missing',
|
|
85
|
+
'card_duplicate',
|
|
86
|
+
'card_not_verified',
|
|
87
|
+
'already_assigned',
|
|
88
|
+
];
|
|
89
|
+
export function istPartnerFehlerCode(wert) {
|
|
90
|
+
return typeof wert === 'string' && PARTNER_FEHLER_CODES.includes(wert);
|
|
91
|
+
}
|
|
92
|
+
export function istPartnerPortalFehlerCode(wert) {
|
|
93
|
+
return typeof wert === 'string' && PARTNER_PORTAL_FEHLER_CODES.includes(wert);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Was der Aufrufer tun muss. Ein Satz je Code, in der zweiten Person — nicht
|
|
97
|
+
* die Wiederholung der Server-Meldung, sondern der naechste Handgriff.
|
|
98
|
+
*
|
|
99
|
+
* Jeder Code des Katalogs steht hier; `partner-client.test.ts` haelt das fest.
|
|
100
|
+
* Ein Code ohne Satz waere schlimmer als ein fehlender Code: er sieht aus wie
|
|
101
|
+
* behandelt und sagt nichts.
|
|
102
|
+
*/
|
|
103
|
+
const RAT = {
|
|
104
|
+
// -- Schnittstelle --------------------------------------------------------
|
|
105
|
+
validation: 'Eingaben pruefen — data.errors nennt Feld und Grund, verschachtelt mit vollem Pfad (address.zip, contacts.0.email). Auch ein UNBEKANNTES Feld ist ein Formfehler: es wird abgewiesen und nicht stillschweigend verworfen. Es wurde nichts angelegt.',
|
|
106
|
+
rate_limited: 'Zu viele Aufrufe. data.retryAfterSec Sekunden warten und denselben Aufruf wiederholen.',
|
|
107
|
+
app_not_found: 'Die appId gibt es nicht. getPartnerInfo liefert die eigenen Apps samt id.',
|
|
108
|
+
app_not_accepted: 'Diese App hat noch keine abgenommene Version. Mit einem pk_test_-Schluessel oder mit env:"test" geht es sofort weiter; live erst nach der Abnahme.',
|
|
109
|
+
kein_partnerbetrieb: 'Dieser Betrieb gehoert nicht zu diesem Partner-Konto. Die eigenen stehen in listPartnerCustomers.',
|
|
110
|
+
live_not_allowed: 'Ein Test-Schluessel erzeugt nichts Echtes. Fuer einen Live-Betrieb den Live-Schluessel nehmen — umgekehrt darf ein Live-Schluessel mit env:"test" sehr wohl einen Testbetrieb anlegen.',
|
|
111
|
+
customer_exists: 'Diesen Betrieb gibt es schon (data.customerId). Mit derselben customerId weiterarbeiten.',
|
|
112
|
+
customer_conflict: 'Die Steuernummer ist bei Kasseneck bereits registriert. Die Zuordnung zum Partner macht Kasseneck — hello@kasseneck.at.',
|
|
113
|
+
customer_limit: 'Das Tageslimit fuer neue Betriebe ist erreicht (data.max, data.resetAt). Morgen weiter.',
|
|
114
|
+
zugang_nicht_erlaubt: 'Fuer dieses Partner-Konto sind Zugaenge zum Kundenpanel nicht freigeschaltet — es entstand NICHTS, auch kein Betrieb. Ohne zugang{invite:true} erneut anlegen oder die Freischaltung erfragen (Stand: getPartnerInfo.partner.canCreateAccess).',
|
|
115
|
+
email_taken: 'Fuer diese E-Mail gibt es schon einen Kasseneck-Zugang. Eine andere Adresse waehlen, auf die Einladung verzichten oder den Betrieb zuordnen lassen.',
|
|
116
|
+
no_email: 'Im Konto des Betriebs steht keine E-Mail-Adresse. Ohne sie geht weder eine Einladung noch der FinanzOnline-Link hinaus.',
|
|
117
|
+
fon_missing: 'Der Betrieb hat noch keinen FinanzOnline-Zugang. sendPartnerCustomerFonLink senden und customer.fon_verified abwarten. Betrifft das ANMELDEN der Signatureinheit, nicht das Beantragen.',
|
|
118
|
+
signature_pending: 'Fuer diesen Betrieb laeuft bereits ein Antrag. Auf signature.ready warten.',
|
|
119
|
+
request_not_found: 'Diese signaturId gibt es nicht. getCustomerSignatureStatus nennt die des Betriebs.',
|
|
120
|
+
signature_missing: 'Der Betrieb hat ueberhaupt keine Signatur, und jede Kasse bezieht sich auf eine. Zuerst requestCustomerSignature.',
|
|
121
|
+
signature_unknown: 'Die genannte signaturId gehoert nicht zu diesem Betrieb. getCustomerSignatureStatus nennt die seinen.',
|
|
122
|
+
signature_ambiguous: 'Der Betrieb hat mehrere Signaturen; welche die Kasse benutzt, muss dastehen. Eine aus data.choices als signaturId mitgeben.',
|
|
123
|
+
signature_not_ready: 'Die Signatur DIESER Kasse ist noch nicht bereit. Auf signature.ready warten; eine mit automatic:true angelegte Kasse geht danach von selbst live.',
|
|
124
|
+
signature_limit: 'Hoechstens zehn Signaturen je Betrieb. Eine bestehende benutzen, statt mit additional:true eine weitere zu beantragen.',
|
|
125
|
+
signature_failed: 'FinanzOnline hat die Anmeldung abgelehnt (data.rc). Kasseneck klaert das — hello@kasseneck.at.',
|
|
126
|
+
module_inactive: 'Das Modul (data.modul) ist fuer diesen Betrieb nicht gebucht. Kasseneck schaltet es frei.',
|
|
127
|
+
cashregister_limit: 'Hoechstens 20 Registrierkassen je Betrieb. Eine bestehende nutzen.',
|
|
128
|
+
cashregister_not_found: 'Diese cashregisterId gibt es bei diesem Betrieb nicht.',
|
|
129
|
+
activation_failed: 'Die Inbetriebnahme blieb an data.step haengen (ggf. data.rc). activateCashregister erneut aufrufen — jeder Schritt ist idempotent, der Lauf setzt an der Bruchstelle an.',
|
|
130
|
+
webhook_limit: 'Hoechstens 10 Webhook-Endpunkte je Partner. Einen ungenutzten loeschen.',
|
|
131
|
+
webhook_inactive: 'Der Webhook steht auf active:false. Zuerst aktivieren, dann erneut proben.',
|
|
132
|
+
event_not_subscribed: 'Der Endpunkt abonniert dieses Ereignis nicht — auch eine Probe bekommt nur, was in seiner events-Liste steht. events erweitern und erneut versuchen.',
|
|
133
|
+
// -- Partner-Portal -------------------------------------------------------
|
|
134
|
+
app_locked: 'Name, Verteilungen und Kontakt einer App sind fest, sobald eine Version geprueft wird. Aenderungen daran gehen ueber Kasseneck.',
|
|
135
|
+
version_locked: 'Diese App-Version wird geprueft oder ist abgenommen. Fuer Aenderungen eine neue Version anlegen.',
|
|
136
|
+
invalid_transition: 'Dieser Statuswechsel ist nicht vorgesehen. Den geltenden Stand laden und von dort weitergehen.',
|
|
137
|
+
no_accepted_app: 'Einen Live-Schluessel gibt es erst nach der Abnahme einer App. Bis dahin mit dem pk_test_-Schluessel arbeiten.',
|
|
138
|
+
consent: 'Der Datenschutzhinweis wurde nicht bestaetigt. Ohne die Bestaetigung entsteht nichts.',
|
|
139
|
+
key_limit: 'Mehr aktive Schluessel je Umgebung als erlaubt. Zuerst einen widerrufen, dann einen neuen erzeugen.',
|
|
140
|
+
last_owner: 'Der letzte Inhaber eines Partner-Kontos laesst sich nicht entfernen. Zuerst einen zweiten ernennen.',
|
|
141
|
+
auth_user_exists: 'Diese E-Mail-Adresse ist bereits einem Konto zugeordnet. Eine andere waehlen.',
|
|
142
|
+
card_missing: 'Zu diesem Antrag sind noch keine Kartendaten eingetragen.',
|
|
143
|
+
card_duplicate: 'Diese Seriennummer ist bei Kasseneck schon eingetragen — die Karte ist bereits erfasst.',
|
|
144
|
+
card_not_verified: 'Die Kartendaten sind noch nicht geprueft. Die Pruefung abwarten (data.request).',
|
|
145
|
+
already_assigned: 'Fuer diesen Antrag sind bereits Kartendaten eingetragen; ein zweiter Satz ueberschreibt nichts.',
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Der Handlungssatz zu einem Code — aus beiden Flaechen. `undefined` fuer
|
|
149
|
+
* einen Code, den dieses Paket nicht kennt; ein erfundener Satz waere
|
|
150
|
+
* schlimmer als keiner.
|
|
151
|
+
*/
|
|
152
|
+
export function partnerFehlerRat(code) {
|
|
153
|
+
return RAT[code];
|
|
154
|
+
}
|
|
155
|
+
/** Der Fehlercode eines geworfenen Fehlers — `undefined`, wenn es keiner der unseren ist. */
|
|
156
|
+
export function partnerFehlerCode(error) {
|
|
157
|
+
return error instanceof KasseneckApiError ? error.code : undefined;
|
|
158
|
+
}
|
|
159
|
+
/** Kurzform fuer `catch (e) { if (istPartnerFehler(e, 'signature_missing')) … }`. */
|
|
160
|
+
export function istPartnerFehler(error, code) {
|
|
161
|
+
return partnerFehlerCode(error) === code;
|
|
162
|
+
}
|
|
163
|
+
/** Die Feldfehler einer `validation`-Antwort; leer, wenn es keine sind. */
|
|
164
|
+
export function partnerFeldFehler(error) {
|
|
165
|
+
if (!(error instanceof KasseneckApiError))
|
|
166
|
+
return [];
|
|
167
|
+
const roh = error.details['errors'];
|
|
168
|
+
if (!Array.isArray(roh))
|
|
169
|
+
return [];
|
|
170
|
+
const raus = [];
|
|
171
|
+
for (const eintrag of roh) {
|
|
172
|
+
if (eintrag === null || typeof eintrag !== 'object')
|
|
173
|
+
continue;
|
|
174
|
+
const { field, message } = eintrag;
|
|
175
|
+
if (typeof field === 'string' && typeof message === 'string')
|
|
176
|
+
raus.push({ field, message });
|
|
177
|
+
}
|
|
178
|
+
return raus;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Wie lange `rate_limited` noch gilt, in Sekunden. `undefined`, wenn der
|
|
182
|
+
* Fehler kein `rate_limited` ist oder das Backend keine Angabe macht.
|
|
183
|
+
*/
|
|
184
|
+
export function partnerWartezeitSek(error) {
|
|
185
|
+
if (partnerFehlerCode(error) !== 'rate_limited')
|
|
186
|
+
return undefined;
|
|
187
|
+
const wert = error.details['retryAfterSec'];
|
|
188
|
+
return typeof wert === 'number' && Number.isFinite(wert) && wert >= 0 ? wert : undefined;
|
|
189
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@kreiseck/kasseneck-api/partner` — alles, was ein Partner-Softwarehaus
|
|
3
|
+
* ueber die Kasseneck-Schnittstelle tut.
|
|
4
|
+
*
|
|
5
|
+
* Ein eigener Unterpfad und nicht die Wurzel, aus zwei Gruenden: der
|
|
6
|
+
* Partner-Schluessel gehoert auf einen **Server** (er kann Betriebe anlegen und
|
|
7
|
+
* deren Geheimnisse holen), und die Kassen-Seite des Pakets soll ihn nicht
|
|
8
|
+
* versehentlich in ein Browser-Buendel ziehen.
|
|
9
|
+
*
|
|
10
|
+
* Die Beschreibung der Endpunkte steht **nicht** hier, sondern in der Referenz
|
|
11
|
+
* des Backends (`docs/api/partner.md`, kompakt `docs/api/partner.llms.txt`).
|
|
12
|
+
* Was hier steht, ist die Benutzung dieses Clients.
|
|
13
|
+
*
|
|
14
|
+
* Reihenfolge der Kette: [PARTNER_ABLAUF].
|
|
15
|
+
*/
|
|
16
|
+
export { createPartnerApi, type PartnerApi, type PartnerApiOptions } from './api.js';
|
|
17
|
+
export { partnerKeyAuth, partnerKeyEnv, type PartnerKeyAuthOptions } from './auth.js';
|
|
18
|
+
export { PARTNER_ABLAUF, naechsterSchritt, type AblaufSchritt, } from './ablauf.js';
|
|
19
|
+
export { PARTNER_FEHLER_CODES, PARTNER_PORTAL_FEHLER_CODES, istPartnerFehlerCode, istPartnerPortalFehlerCode, istPartnerFehler, partnerFehlerCode, partnerFehlerRat, partnerFeldFehler, partnerWartezeitSek, type PartnerCode, type PartnerFehlerCode, type PartnerPortalFehlerCode, type PartnerFeldFehler, } from './fehler.js';
|
|
20
|
+
export { BETRIEB_FELDER, unbekannteBetriebsfelder, type BetriebFeld, } from './betrieb.js';
|
|
21
|
+
export { KasseneckSecret, SECRET_MASKE } from './secret.js';
|
|
22
|
+
export { getPartnerInfo, createPartnerCustomer, listPartnerCustomers, checkPartnerCustomerEmail, getPartnerCustomer, sendPartnerCustomerFonLink, requestCustomerSignature, getCustomerSignatureStatus, createCustomerCashregister, activateCashregister, listCustomerCashregisters, getCustomerCredentials, } from './endpunkte.js';
|
|
23
|
+
export { createPartnerWebhook, listPartnerWebhooks, rotatePartnerWebhookSecret, updatePartnerWebhook, deletePartnerWebhook, sendPartnerWebhookTest, listPartnerWebhookDeliveries, parseWebhookEvent, istPartnerWebhookEvent, PARTNER_WEBHOOK_EVENTS, WEBHOOK_UMSCHLAG_FELDER, type PartnerWebhookEvent, type PartnerWebhookEventType, type PartnerWebhook, type CreateWebhookOptions, type CreateWebhookResult, type WebhookPatch, type WebhookListe, type WebhookZustellung, type WebhookTestResult, type WebhookEventResult, } from './webhooks.js';
|
|
24
|
+
export { verifyWebhookSignature, parseSignatureHeader, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_EVENT_HEADER, WEBHOOK_DELIVERY_HEADER, WEBHOOK_TOLERANCE_SEC, WEBHOOK_RETRY_PLAN_SEC, WEBHOOK_MAX_ATTEMPTS, WEBHOOK_TIMEOUT_MS, WEBHOOK_LIMIT, type VerifyWebhookOptions, type WebhookVerifyResult, type WebhookVerifyReason, } from './webhook-signatur.js';
|
|
25
|
+
export { PARTNER_ENVS } from './typen.js';
|
|
26
|
+
export type { PartnerEnv, PartnerScope, PartnerApp, PartnerInfo, Rechtsform, Bundesland, KontaktRolle, BetriebAdresse, BetriebSteuer, BetriebKontakt, BetriebSteuerberater, Betrieb, CreateCustomerOptions, CreateCustomerResult, KundenStatus, KundenZeile, AvvStand, ListCustomersOptions, KundenListe, Kunde, FonLinkResult, SignaturAntragStatus, SignaturHistorieEintrag, SignaturAntrag, RequestSignatureResult, SignaturStand, KassenSchritt, KassenStatus, Kasse, CreateCashregisterOptions, CreateCashregisterResult, ActivateCashregisterResult, KassenListe, CustomerCashregisterCredential, CustomerCredentials, } from './typen.js';
|
|
27
|
+
/** `credentials:read` — nicht im Standardsatz, siehe typen.ts. */
|
|
28
|
+
export { SCOPE_CREDENTIALS } from './typen.js';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@kreiseck/kasseneck-api/partner` — alles, was ein Partner-Softwarehaus
|
|
3
|
+
* ueber die Kasseneck-Schnittstelle tut.
|
|
4
|
+
*
|
|
5
|
+
* Ein eigener Unterpfad und nicht die Wurzel, aus zwei Gruenden: der
|
|
6
|
+
* Partner-Schluessel gehoert auf einen **Server** (er kann Betriebe anlegen und
|
|
7
|
+
* deren Geheimnisse holen), und die Kassen-Seite des Pakets soll ihn nicht
|
|
8
|
+
* versehentlich in ein Browser-Buendel ziehen.
|
|
9
|
+
*
|
|
10
|
+
* Die Beschreibung der Endpunkte steht **nicht** hier, sondern in der Referenz
|
|
11
|
+
* des Backends (`docs/api/partner.md`, kompakt `docs/api/partner.llms.txt`).
|
|
12
|
+
* Was hier steht, ist die Benutzung dieses Clients.
|
|
13
|
+
*
|
|
14
|
+
* Reihenfolge der Kette: [PARTNER_ABLAUF].
|
|
15
|
+
*/
|
|
16
|
+
export { createPartnerApi } from './api.js';
|
|
17
|
+
export { partnerKeyAuth, partnerKeyEnv } from './auth.js';
|
|
18
|
+
export { PARTNER_ABLAUF, naechsterSchritt, } from './ablauf.js';
|
|
19
|
+
export { PARTNER_FEHLER_CODES, PARTNER_PORTAL_FEHLER_CODES, istPartnerFehlerCode, istPartnerPortalFehlerCode, istPartnerFehler, partnerFehlerCode, partnerFehlerRat, partnerFeldFehler, partnerWartezeitSek, } from './fehler.js';
|
|
20
|
+
export { BETRIEB_FELDER, unbekannteBetriebsfelder, } from './betrieb.js';
|
|
21
|
+
export { KasseneckSecret, SECRET_MASKE } from './secret.js';
|
|
22
|
+
export { getPartnerInfo, createPartnerCustomer, listPartnerCustomers, checkPartnerCustomerEmail, getPartnerCustomer, sendPartnerCustomerFonLink, requestCustomerSignature, getCustomerSignatureStatus, createCustomerCashregister, activateCashregister, listCustomerCashregisters, getCustomerCredentials, } from './endpunkte.js';
|
|
23
|
+
export { createPartnerWebhook, listPartnerWebhooks, rotatePartnerWebhookSecret, updatePartnerWebhook, deletePartnerWebhook, sendPartnerWebhookTest, listPartnerWebhookDeliveries, parseWebhookEvent, istPartnerWebhookEvent, PARTNER_WEBHOOK_EVENTS, WEBHOOK_UMSCHLAG_FELDER, } from './webhooks.js';
|
|
24
|
+
export { verifyWebhookSignature, parseSignatureHeader, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_EVENT_HEADER, WEBHOOK_DELIVERY_HEADER, WEBHOOK_TOLERANCE_SEC, WEBHOOK_RETRY_PLAN_SEC, WEBHOOK_MAX_ATTEMPTS, WEBHOOK_TIMEOUT_MS, WEBHOOK_LIMIT, } from './webhook-signatur.js';
|
|
25
|
+
export { PARTNER_ENVS } from './typen.js';
|
|
26
|
+
/** `credentials:read` — nicht im Standardsatz, siehe typen.ts. */
|
|
27
|
+
export { SCOPE_CREDENTIALS } from './typen.js';
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ein Geheimnis eines fremden Betriebs — der `api_key` und die Kassen-Token,
|
|
3
|
+
* die `getCustomerCredentials` liefert.
|
|
4
|
+
*
|
|
5
|
+
* **Warum ein eigener Typ und nicht `string`:** diese Werte gehoeren einem
|
|
6
|
+
* Dritten. Wer sie hat, kann in seinem Namen Belege signieren — und ein Beleg
|
|
7
|
+
* ist nach RKSV nicht zuruecknehmbar. Ein `string` in einem Antwortobjekt
|
|
8
|
+
* landet aber genau dort, wo Objekte nun einmal landen: in
|
|
9
|
+
* `console.log(antwort)`, in `JSON.stringify(antwort)` unter einem Fehler, im
|
|
10
|
+
* Rumpf eines Fehlerberichts, in einer Mail an den Kunden. Kein einziger
|
|
11
|
+
* dieser Wege ist boese gemeint, und jeder einzelne gibt den Schluessel weiter.
|
|
12
|
+
*
|
|
13
|
+
* Deshalb kommt man hier nur ueber **einen** benannten Weg an den Wert:
|
|
14
|
+
* [reveal]. Der Name ist mit Absicht so gewaehlt, dass eine Suche nach
|
|
15
|
+
* `.reveal(` in einer fremden Codebasis genau die Stellen zeigt, an denen ein
|
|
16
|
+
* Geheimnis das Objekt verlaesst.
|
|
17
|
+
*
|
|
18
|
+
* **Wie die Maskierung haelt.** Der Wert liegt in einer `WeakMap` neben dem
|
|
19
|
+
* Objekt, nicht *im* Objekt: die Instanz hat kein einziges eigenes Feld mit
|
|
20
|
+
* dem Klartext, und was nicht da ist, kann auch kein Ausgabeweg finden — auch
|
|
21
|
+
* keiner, den dieses Paket nicht kennt. Die Ueberschreibungen von `toString`,
|
|
22
|
+
* `toJSON`, `Symbol.toPrimitive` und dem Node-Inspektor kommen **zusaetzlich**,
|
|
23
|
+
* damit die Maske nicht als `[object Object]` erscheint, sondern als Satz, der
|
|
24
|
+
* sagt, was fehlt und warum.
|
|
25
|
+
*
|
|
26
|
+
* Ein privates Klassenfeld (`#wert`) waere die naheliegende Alternative und
|
|
27
|
+
* reicht nicht: `util.inspect` zeigt private Felder in neueren Node-Fassungen
|
|
28
|
+
* an, und ein Fehlerdienst, der ein Objekt tief durchlaeuft, kommt ohnehin nur
|
|
29
|
+
* an das, was am Objekt haengt. Die WeakMap loest beides auf einmal.
|
|
30
|
+
*/
|
|
31
|
+
/** Wie ein maskiertes Geheimnis in Text erscheint. */
|
|
32
|
+
export declare const SECRET_MASKE = "\u00ABverborgen\u00BB";
|
|
33
|
+
export declare class KasseneckSecret {
|
|
34
|
+
/**
|
|
35
|
+
* Wofuer dieses Geheimnis steht (`apiKey`, `cashregisterToken`). Kein
|
|
36
|
+
* Geheimnis, nur eine Beschriftung — sie steht in der Maske, damit ein
|
|
37
|
+
* Protokoll erkennen laesst, WELCHER Wert fehlt.
|
|
38
|
+
*/
|
|
39
|
+
readonly label: string;
|
|
40
|
+
constructor(label: string, wert: string);
|
|
41
|
+
/**
|
|
42
|
+
* Der Klartext. Der einzige Weg heraus — und die Stelle, an der ein
|
|
43
|
+
* Aufrufer sich entscheidet, das Geheimnis weiterzugeben.
|
|
44
|
+
*
|
|
45
|
+
* Verschluesselt speichern. Nie protokollieren, nie in eine Mail, nie in
|
|
46
|
+
* einen Fehlerbericht.
|
|
47
|
+
*/
|
|
48
|
+
reveal(): string;
|
|
49
|
+
/** Ob ueberhaupt ein Wert da ist — ohne ihn anzufassen. */
|
|
50
|
+
get vorhanden(): boolean;
|
|
51
|
+
toString(): string;
|
|
52
|
+
/**
|
|
53
|
+
* Greift bei `JSON.stringify` — dem Weg, auf dem ein Geheimnis am
|
|
54
|
+
* unauffaelligsten in ein Protokoll rutscht.
|
|
55
|
+
*/
|
|
56
|
+
toJSON(): string;
|
|
57
|
+
/**
|
|
58
|
+
* Greift bei `` `${geheimnis}` `` und bei `'' + geheimnis`. Ohne diese
|
|
59
|
+
* Ueberschreibung stuende zwar dasselbe wie in [toString], aber
|
|
60
|
+
* `Symbol.toPrimitive` hat Vorrang — wer ihn spaeter versehentlich anders
|
|
61
|
+
* belegt, umgeht die Maske.
|
|
62
|
+
*/
|
|
63
|
+
[Symbol.toPrimitive](): string;
|
|
64
|
+
}
|
|
65
|
+
/** Baut ein Geheimnis aus einem Antwortfeld; fehlt es, entsteht ein leeres. */
|
|
66
|
+
export declare function alsSecret(label: string, wert: unknown): KasseneckSecret;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ein Geheimnis eines fremden Betriebs — der `api_key` und die Kassen-Token,
|
|
3
|
+
* die `getCustomerCredentials` liefert.
|
|
4
|
+
*
|
|
5
|
+
* **Warum ein eigener Typ und nicht `string`:** diese Werte gehoeren einem
|
|
6
|
+
* Dritten. Wer sie hat, kann in seinem Namen Belege signieren — und ein Beleg
|
|
7
|
+
* ist nach RKSV nicht zuruecknehmbar. Ein `string` in einem Antwortobjekt
|
|
8
|
+
* landet aber genau dort, wo Objekte nun einmal landen: in
|
|
9
|
+
* `console.log(antwort)`, in `JSON.stringify(antwort)` unter einem Fehler, im
|
|
10
|
+
* Rumpf eines Fehlerberichts, in einer Mail an den Kunden. Kein einziger
|
|
11
|
+
* dieser Wege ist boese gemeint, und jeder einzelne gibt den Schluessel weiter.
|
|
12
|
+
*
|
|
13
|
+
* Deshalb kommt man hier nur ueber **einen** benannten Weg an den Wert:
|
|
14
|
+
* [reveal]. Der Name ist mit Absicht so gewaehlt, dass eine Suche nach
|
|
15
|
+
* `.reveal(` in einer fremden Codebasis genau die Stellen zeigt, an denen ein
|
|
16
|
+
* Geheimnis das Objekt verlaesst.
|
|
17
|
+
*
|
|
18
|
+
* **Wie die Maskierung haelt.** Der Wert liegt in einer `WeakMap` neben dem
|
|
19
|
+
* Objekt, nicht *im* Objekt: die Instanz hat kein einziges eigenes Feld mit
|
|
20
|
+
* dem Klartext, und was nicht da ist, kann auch kein Ausgabeweg finden — auch
|
|
21
|
+
* keiner, den dieses Paket nicht kennt. Die Ueberschreibungen von `toString`,
|
|
22
|
+
* `toJSON`, `Symbol.toPrimitive` und dem Node-Inspektor kommen **zusaetzlich**,
|
|
23
|
+
* damit die Maske nicht als `[object Object]` erscheint, sondern als Satz, der
|
|
24
|
+
* sagt, was fehlt und warum.
|
|
25
|
+
*
|
|
26
|
+
* Ein privates Klassenfeld (`#wert`) waere die naheliegende Alternative und
|
|
27
|
+
* reicht nicht: `util.inspect` zeigt private Felder in neueren Node-Fassungen
|
|
28
|
+
* an, und ein Fehlerdienst, der ein Objekt tief durchlaeuft, kommt ohnehin nur
|
|
29
|
+
* an das, was am Objekt haengt. Die WeakMap loest beides auf einmal.
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* Der Klartext, ausserhalb der Instanz. `WeakMap`, damit ein weggeworfenes
|
|
33
|
+
* Geheimnis samt Wert eingesammelt werden kann.
|
|
34
|
+
*/
|
|
35
|
+
const werte = new WeakMap();
|
|
36
|
+
/** Wie ein maskiertes Geheimnis in Text erscheint. */
|
|
37
|
+
export const SECRET_MASKE = '«verborgen»';
|
|
38
|
+
export class KasseneckSecret {
|
|
39
|
+
/**
|
|
40
|
+
* Wofuer dieses Geheimnis steht (`apiKey`, `cashregisterToken`). Kein
|
|
41
|
+
* Geheimnis, nur eine Beschriftung — sie steht in der Maske, damit ein
|
|
42
|
+
* Protokoll erkennen laesst, WELCHER Wert fehlt.
|
|
43
|
+
*/
|
|
44
|
+
label;
|
|
45
|
+
constructor(label, wert) {
|
|
46
|
+
this.label = label;
|
|
47
|
+
werte.set(this, wert);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Der Klartext. Der einzige Weg heraus — und die Stelle, an der ein
|
|
51
|
+
* Aufrufer sich entscheidet, das Geheimnis weiterzugeben.
|
|
52
|
+
*
|
|
53
|
+
* Verschluesselt speichern. Nie protokollieren, nie in eine Mail, nie in
|
|
54
|
+
* einen Fehlerbericht.
|
|
55
|
+
*/
|
|
56
|
+
reveal() {
|
|
57
|
+
return werte.get(this) ?? '';
|
|
58
|
+
}
|
|
59
|
+
/** Ob ueberhaupt ein Wert da ist — ohne ihn anzufassen. */
|
|
60
|
+
get vorhanden() {
|
|
61
|
+
return (werte.get(this) ?? '').length > 0;
|
|
62
|
+
}
|
|
63
|
+
toString() {
|
|
64
|
+
return `[${this.label} ${SECRET_MASKE}]`;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Greift bei `JSON.stringify` — dem Weg, auf dem ein Geheimnis am
|
|
68
|
+
* unauffaelligsten in ein Protokoll rutscht.
|
|
69
|
+
*/
|
|
70
|
+
toJSON() {
|
|
71
|
+
return this.toString();
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Greift bei `` `${geheimnis}` `` und bei `'' + geheimnis`. Ohne diese
|
|
75
|
+
* Ueberschreibung stuende zwar dasselbe wie in [toString], aber
|
|
76
|
+
* `Symbol.toPrimitive` hat Vorrang — wer ihn spaeter versehentlich anders
|
|
77
|
+
* belegt, umgeht die Maske.
|
|
78
|
+
*/
|
|
79
|
+
[Symbol.toPrimitive]() {
|
|
80
|
+
return this.toString();
|
|
81
|
+
}
|
|
82
|
+
/** Greift bei `console.log`, `util.inspect` und den meisten Fehlerdiensten. */
|
|
83
|
+
[Symbol.for('nodejs.util.inspect.custom')]() {
|
|
84
|
+
return this.toString();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/** Baut ein Geheimnis aus einem Antwortfeld; fehlt es, entsteht ein leeres. */
|
|
88
|
+
export function alsSecret(label, wert) {
|
|
89
|
+
return new KasseneckSecret(label, typeof wert === 'string' ? wert : '');
|
|
90
|
+
}
|