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