@kreiseck/kasseneck-api 0.6.46 → 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 +19 -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/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/esm/client/aufrufe.d.ts +1 -1
- package/dist/esm/client/aufrufe.js +19 -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/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/fixtures/oberflaeche.json +131 -3
- package/package.json +13 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Der Weg vom Vertragsabschluss bis zum ersten Beleg — als Daten, nicht als
|
|
3
|
+
* Fliesstext.
|
|
4
|
+
*
|
|
5
|
+
* **Warum das im Paket steht und nicht nur in der Doku:** die Kette hat eine
|
|
6
|
+
* harte Reihenfolge, und jeder Schritt scheitert mit einem eigenen Code, wenn
|
|
7
|
+
* ein vorheriger fehlt (`fon_missing`, `signature_missing`,
|
|
8
|
+
* `signature_not_ready`).
|
|
9
|
+
* Wer die Reihenfolge nur aus einer Fehlermeldung lernt, lernt sie einmal je
|
|
10
|
+
* Fehler. Hier steht sie vorher — abfragbar, ausgebbar, und in
|
|
11
|
+
* [naechsterSchritt] auch beantwortbar.
|
|
12
|
+
*
|
|
13
|
+
* **Ohne Vertragsschritt.** Auftragsverarbeitungsvertraege wirken in diesem
|
|
14
|
+
* Weg nicht mehr (Stand 2026-08-31): kein Aufruf meldet einen, keine Kasse
|
|
15
|
+
* bleibt deswegen stehen.
|
|
16
|
+
*
|
|
17
|
+
* Zwei Dinge laufen bewusst **parallel**: der Signaturantrag und das Anlegen
|
|
18
|
+
* der Kasse. Eine mit `automatic:true` angelegte Kasse wartet, bis die
|
|
19
|
+
* Signatur bereit ist, und geht dann von selbst live. Der Kundenstatus nennt
|
|
20
|
+
* darum immer nur den **weitesten erreichten** Meilenstein, nicht die einzige
|
|
21
|
+
* laufende Arbeit.
|
|
22
|
+
*/
|
|
23
|
+
import type { KundenStatus } from './typen.js';
|
|
24
|
+
/** Ein Schritt der Kette. */
|
|
25
|
+
export interface AblaufSchritt {
|
|
26
|
+
key: string;
|
|
27
|
+
/** Was in diesem Schritt passiert. */
|
|
28
|
+
text: string;
|
|
29
|
+
/** Der Aufruf, der ihn ausloest — `null`, wenn hier nur gewartet wird. */
|
|
30
|
+
aufruf: string | null;
|
|
31
|
+
/** Das Ereignis, das seinen Abschluss meldet — `null`, wenn es sofort feststeht. */
|
|
32
|
+
wartetAuf: string | null;
|
|
33
|
+
/** Der Fehlercode, mit dem ein spaeterer Aufruf sich beschwert, wenn dieser Schritt fehlt. */
|
|
34
|
+
fehltCode: string | null;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Die Kette in ihrer Reihenfolge. Sie beschreibt den **Live-Weg**; mit einem
|
|
38
|
+
* `pk_test_`-Schluessel entfallen die FinanzOnline-Schritte, und die Signatur
|
|
39
|
+
* ist sofort bereit (AT100-Testkarte — die damit erzeugten Belege sind keine
|
|
40
|
+
* gueltigen RKSV-Belege).
|
|
41
|
+
*/
|
|
42
|
+
export declare const PARTNER_ABLAUF: readonly AblaufSchritt[];
|
|
43
|
+
/**
|
|
44
|
+
* Der Schritt, an dem ein Betrieb mit diesem Status steht — `null` fuer
|
|
45
|
+
* `gesperrt` und fuer jeden Status, den dieses Paket nicht kennt.
|
|
46
|
+
*/
|
|
47
|
+
export declare function naechsterSchritt(status: KundenStatus): AblaufSchritt | null;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Der Weg vom Vertragsabschluss bis zum ersten Beleg — als Daten, nicht als
|
|
4
|
+
* Fliesstext.
|
|
5
|
+
*
|
|
6
|
+
* **Warum das im Paket steht und nicht nur in der Doku:** die Kette hat eine
|
|
7
|
+
* harte Reihenfolge, und jeder Schritt scheitert mit einem eigenen Code, wenn
|
|
8
|
+
* ein vorheriger fehlt (`fon_missing`, `signature_missing`,
|
|
9
|
+
* `signature_not_ready`).
|
|
10
|
+
* Wer die Reihenfolge nur aus einer Fehlermeldung lernt, lernt sie einmal je
|
|
11
|
+
* Fehler. Hier steht sie vorher — abfragbar, ausgebbar, und in
|
|
12
|
+
* [naechsterSchritt] auch beantwortbar.
|
|
13
|
+
*
|
|
14
|
+
* **Ohne Vertragsschritt.** Auftragsverarbeitungsvertraege wirken in diesem
|
|
15
|
+
* Weg nicht mehr (Stand 2026-08-31): kein Aufruf meldet einen, keine Kasse
|
|
16
|
+
* bleibt deswegen stehen.
|
|
17
|
+
*
|
|
18
|
+
* Zwei Dinge laufen bewusst **parallel**: der Signaturantrag und das Anlegen
|
|
19
|
+
* der Kasse. Eine mit `automatic:true` angelegte Kasse wartet, bis die
|
|
20
|
+
* Signatur bereit ist, und geht dann von selbst live. Der Kundenstatus nennt
|
|
21
|
+
* darum immer nur den **weitesten erreichten** Meilenstein, nicht die einzige
|
|
22
|
+
* laufende Arbeit.
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.PARTNER_ABLAUF = void 0;
|
|
26
|
+
exports.naechsterSchritt = naechsterSchritt;
|
|
27
|
+
/**
|
|
28
|
+
* Die Kette in ihrer Reihenfolge. Sie beschreibt den **Live-Weg**; mit einem
|
|
29
|
+
* `pk_test_`-Schluessel entfallen die FinanzOnline-Schritte, und die Signatur
|
|
30
|
+
* ist sofort bereit (AT100-Testkarte — die damit erzeugten Belege sind keine
|
|
31
|
+
* gueltigen RKSV-Belege).
|
|
32
|
+
*/
|
|
33
|
+
exports.PARTNER_ABLAUF = [
|
|
34
|
+
{
|
|
35
|
+
key: 'business',
|
|
36
|
+
text: 'Betrieb anlegen (idempotencyKey = eigene Kundennummer).',
|
|
37
|
+
aufruf: 'createPartnerCustomer',
|
|
38
|
+
wartetAuf: 'customer.created',
|
|
39
|
+
fehltCode: null,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
key: 'fon',
|
|
43
|
+
text: 'FinanzOnline einrichten: Link an den Betrieb, der Betrieb traegt seinen Zugang ein.',
|
|
44
|
+
aufruf: 'sendPartnerCustomerFonLink',
|
|
45
|
+
wartetAuf: 'customer.fon_verified',
|
|
46
|
+
fehltCode: 'fon_missing',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
key: 'signature',
|
|
50
|
+
text: 'Signatureinheit beantragen. Kasseneck weist eine Karte zu und meldet sie bei FinanzOnline an.',
|
|
51
|
+
aufruf: 'requestCustomerSignature',
|
|
52
|
+
wartetAuf: 'signature.ready',
|
|
53
|
+
fehltCode: 'signature_missing',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
key: 'cashregister',
|
|
57
|
+
text: 'Kasse anlegen. Mit automatic:true (Vorgabe) geht sie von selbst live, sobald die Signatur bereit ist — ' +
|
|
58
|
+
'sie darf deshalb schon vorher angelegt werden.',
|
|
59
|
+
aufruf: 'createCustomerCashregister',
|
|
60
|
+
wartetAuf: 'cashregister.live',
|
|
61
|
+
fehltCode: 'cashregister_not_found',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
key: 'zugangsdaten',
|
|
65
|
+
text: 'Zugangsdaten des Betriebs holen (Scope credentials:read). Geheimnisse — nur verschluesselt speichern.',
|
|
66
|
+
aufruf: 'getCustomerCredentials',
|
|
67
|
+
wartetAuf: null,
|
|
68
|
+
fehltCode: null,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
key: 'belege',
|
|
72
|
+
text: 'Belege signieren: Bearer = apiKey des Betriebs, Kopfzeile cashregister-token = Token der Kasse.',
|
|
73
|
+
aufruf: 'createReceipt',
|
|
74
|
+
wartetAuf: null,
|
|
75
|
+
fehltCode: null,
|
|
76
|
+
},
|
|
77
|
+
];
|
|
78
|
+
/**
|
|
79
|
+
* Welcher Meilenstein einem Kundenstatus entspricht. Die Zuordnung ist
|
|
80
|
+
* absichtlich grob: der Status nennt den weitesten erreichten Punkt, nicht die
|
|
81
|
+
* laufende Arbeit — fuer den genauen Verlauf sind die Ereignisse `signature.*`
|
|
82
|
+
* und `cashregister.*` da.
|
|
83
|
+
*/
|
|
84
|
+
const STATUS_SCHRITT = {
|
|
85
|
+
created: 'fon',
|
|
86
|
+
fon_configured: 'signature',
|
|
87
|
+
signature_requested: 'signature',
|
|
88
|
+
signature_ready: 'cashregister',
|
|
89
|
+
cashregister_created: 'cashregister',
|
|
90
|
+
live: 'zugangsdaten',
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Der Schritt, an dem ein Betrieb mit diesem Status steht — `null` fuer
|
|
94
|
+
* `gesperrt` und fuer jeden Status, den dieses Paket nicht kennt.
|
|
95
|
+
*/
|
|
96
|
+
function naechsterSchritt(status) {
|
|
97
|
+
const key = STATUS_SCHRITT[status];
|
|
98
|
+
return key ? exports.PARTNER_ABLAUF.find((s) => s.key === key) ?? null : null;
|
|
99
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fassade ueber den Partner-Aufrufen: Schluessel einmal binden, dann rufen.
|
|
3
|
+
*
|
|
4
|
+
* Wie [createKasseneckApi] bewusst **keine** Klasse — die Aufrufe sind freie
|
|
5
|
+
* Funktionen und bleiben einzeln importierbar.
|
|
6
|
+
*/
|
|
7
|
+
import { type FetchLike } from '../client/transport.js';
|
|
8
|
+
import { type CreateWebhookOptions, type CreateWebhookResult, type PartnerWebhook, type WebhookListe, type PartnerWebhookEventType, type WebhookPatch, type WebhookTestResult, type WebhookZustellung } from './webhooks.js';
|
|
9
|
+
import type { ActivateCashregisterResult, CreateCashregisterOptions, CreateCashregisterResult, CreateCustomerOptions, CreateCustomerResult, CustomerCredentials, FonLinkResult, KassenListe, Kunde, KundenListe, ListCustomersOptions, PartnerInfo, RequestSignatureResult, SignaturStand } from './typen.js';
|
|
10
|
+
export interface PartnerApiOptions {
|
|
11
|
+
/** Partner-Schluessel `pk_test_…` / `pk_live_…`. Gehoert auf einen Server. */
|
|
12
|
+
partnerKey: string;
|
|
13
|
+
/** Abweichende Basis-URL; Vorgabe `https://api.kasseneck.at/v1`. */
|
|
14
|
+
baseUrl?: string;
|
|
15
|
+
/** Zeitlimit je Aufruf in Millisekunden. */
|
|
16
|
+
timeoutMs?: number;
|
|
17
|
+
/** Eigene `fetch`-Umsetzung (Tests, Proxys). */
|
|
18
|
+
fetch?: FetchLike;
|
|
19
|
+
}
|
|
20
|
+
export interface PartnerApi {
|
|
21
|
+
getPartnerInfo(): Promise<PartnerInfo>;
|
|
22
|
+
createPartnerCustomer(optionen: CreateCustomerOptions): Promise<CreateCustomerResult>;
|
|
23
|
+
listPartnerCustomers(optionen?: ListCustomersOptions): Promise<KundenListe>;
|
|
24
|
+
getPartnerCustomer(customerId: string): Promise<Kunde>;
|
|
25
|
+
sendPartnerCustomerFonLink(customerId: string): Promise<FonLinkResult>;
|
|
26
|
+
requestCustomerSignature(customerId: string, optionen?: {
|
|
27
|
+
art?: string;
|
|
28
|
+
additional?: boolean;
|
|
29
|
+
}): Promise<RequestSignatureResult>;
|
|
30
|
+
getCustomerSignatureStatus(customerId: string): Promise<SignaturStand>;
|
|
31
|
+
createCustomerCashregister(optionen: CreateCashregisterOptions): Promise<CreateCashregisterResult>;
|
|
32
|
+
activateCashregister(customerId: string, cashregisterId: string): Promise<ActivateCashregisterResult>;
|
|
33
|
+
listCustomerCashregisters(customerId: string): Promise<KassenListe>;
|
|
34
|
+
/** Geheimnisse des Betriebs — siehe `getCustomerCredentials` in endpunkte.ts. */
|
|
35
|
+
getCustomerCredentials(customerId: string): Promise<CustomerCredentials>;
|
|
36
|
+
/**
|
|
37
|
+
* Ist diese Adresse als Kasseneck-Zugang noch frei? Nur noetig, wenn der
|
|
38
|
+
* Betrieb einen eigenen Login bekommen soll — sonst faellt `email_taken`
|
|
39
|
+
* erst nach dem ganzen Formular auf.
|
|
40
|
+
*/
|
|
41
|
+
checkPartnerCustomerEmail(email: string): Promise<boolean>;
|
|
42
|
+
createPartnerWebhook(optionen: CreateWebhookOptions): Promise<CreateWebhookResult>;
|
|
43
|
+
listPartnerWebhooks(): Promise<WebhookListe>;
|
|
44
|
+
updatePartnerWebhook(webhookId: string, patch: WebhookPatch): Promise<PartnerWebhook>;
|
|
45
|
+
deletePartnerWebhook(webhookId: string): Promise<string>;
|
|
46
|
+
/**
|
|
47
|
+
* Neues Secret fuer denselben Endpunkt — dieselbe `webhookId`, dieselben
|
|
48
|
+
* Ereignisse. Das alte gilt ab der Antwort nicht mehr.
|
|
49
|
+
*/
|
|
50
|
+
rotatePartnerWebhookSecret(webhookId: string): Promise<CreateWebhookResult>;
|
|
51
|
+
/**
|
|
52
|
+
* Eine Probe an einen Endpunkt — ohne `event` die Leitungsprobe
|
|
53
|
+
* `webhook.test`, mit `event` genau der Fall, den der Empfaenger behandeln
|
|
54
|
+
* soll. Jede Probe traegt `test: true` im Umschlag.
|
|
55
|
+
*/
|
|
56
|
+
sendPartnerWebhookTest(webhookId: string, event?: PartnerWebhookEventType | (string & {})): Promise<WebhookTestResult>;
|
|
57
|
+
listPartnerWebhookDeliveries(optionen?: {
|
|
58
|
+
webhookId?: string;
|
|
59
|
+
limit?: number;
|
|
60
|
+
}): Promise<WebhookZustellung[]>;
|
|
61
|
+
/**
|
|
62
|
+
* Der Handlungssatz zu einem beliebigen Fehlercode der Partner-API. Gehoert
|
|
63
|
+
* in die eigene Fehlermeldung, damit ein Anwender nicht in der Doku
|
|
64
|
+
* nachschlagen muss.
|
|
65
|
+
*/
|
|
66
|
+
fehlerRat(code: string): string | undefined;
|
|
67
|
+
}
|
|
68
|
+
export declare function createPartnerApi(optionen: PartnerApiOptions): PartnerApi;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Fassade ueber den Partner-Aufrufen: Schluessel einmal binden, dann rufen.
|
|
4
|
+
*
|
|
5
|
+
* Wie [createKasseneckApi] bewusst **keine** Klasse — die Aufrufe sind freie
|
|
6
|
+
* Funktionen und bleiben einzeln importierbar.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.createPartnerApi = createPartnerApi;
|
|
10
|
+
const transport_js_1 = require("../client/transport.js");
|
|
11
|
+
const auth_js_1 = require("./auth.js");
|
|
12
|
+
const fehler_js_1 = require("./fehler.js");
|
|
13
|
+
const endpunkte_js_1 = require("./endpunkte.js");
|
|
14
|
+
const webhooks_js_1 = require("./webhooks.js");
|
|
15
|
+
function createPartnerApi(optionen) {
|
|
16
|
+
const rufen = (0, transport_js_1.createTransport)({
|
|
17
|
+
auth: (0, auth_js_1.partnerKeyAuth)({ partnerKey: optionen.partnerKey }),
|
|
18
|
+
baseUrl: optionen.baseUrl,
|
|
19
|
+
timeoutMs: optionen.timeoutMs,
|
|
20
|
+
fetch: optionen.fetch,
|
|
21
|
+
});
|
|
22
|
+
return {
|
|
23
|
+
getPartnerInfo: () => (0, endpunkte_js_1.getPartnerInfo)(rufen),
|
|
24
|
+
createPartnerCustomer: (o) => (0, endpunkte_js_1.createPartnerCustomer)(rufen, o),
|
|
25
|
+
listPartnerCustomers: (o) => (0, endpunkte_js_1.listPartnerCustomers)(rufen, o),
|
|
26
|
+
getPartnerCustomer: (id) => (0, endpunkte_js_1.getPartnerCustomer)(rufen, id),
|
|
27
|
+
sendPartnerCustomerFonLink: (id) => (0, endpunkte_js_1.sendPartnerCustomerFonLink)(rufen, id),
|
|
28
|
+
requestCustomerSignature: (id, o) => (0, endpunkte_js_1.requestCustomerSignature)(rufen, id, o),
|
|
29
|
+
getCustomerSignatureStatus: (id) => (0, endpunkte_js_1.getCustomerSignatureStatus)(rufen, id),
|
|
30
|
+
createCustomerCashregister: (o) => (0, endpunkte_js_1.createCustomerCashregister)(rufen, o),
|
|
31
|
+
activateCashregister: (kunde, kasse) => (0, endpunkte_js_1.activateCashregister)(rufen, kunde, kasse),
|
|
32
|
+
listCustomerCashregisters: (id) => (0, endpunkte_js_1.listCustomerCashregisters)(rufen, id),
|
|
33
|
+
getCustomerCredentials: (id) => (0, endpunkte_js_1.getCustomerCredentials)(rufen, id),
|
|
34
|
+
checkPartnerCustomerEmail: (email) => (0, endpunkte_js_1.checkPartnerCustomerEmail)(rufen, email),
|
|
35
|
+
createPartnerWebhook: (o) => (0, webhooks_js_1.createPartnerWebhook)(rufen, o),
|
|
36
|
+
listPartnerWebhooks: () => (0, webhooks_js_1.listPartnerWebhooks)(rufen),
|
|
37
|
+
updatePartnerWebhook: (id, patch) => (0, webhooks_js_1.updatePartnerWebhook)(rufen, id, patch),
|
|
38
|
+
deletePartnerWebhook: (id) => (0, webhooks_js_1.deletePartnerWebhook)(rufen, id),
|
|
39
|
+
rotatePartnerWebhookSecret: (id) => (0, webhooks_js_1.rotatePartnerWebhookSecret)(rufen, id),
|
|
40
|
+
sendPartnerWebhookTest: (id, event) => (0, webhooks_js_1.sendPartnerWebhookTest)(rufen, id, event),
|
|
41
|
+
listPartnerWebhookDeliveries: (o) => (0, webhooks_js_1.listPartnerWebhookDeliveries)(rufen, o),
|
|
42
|
+
fehlerRat: (code) => (0, fehler_js_1.partnerFehlerRat)(code),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anmeldung eines Partner-Servers: der Partner-Schluessel als Bearer.
|
|
3
|
+
*
|
|
4
|
+
* Ein dritter Weg neben `apiKeyAuth` (Geraete) und `registerUserAuth`
|
|
5
|
+
* (Browser-Kasse) — und der einzige, mit dem die Partner-Endpunkte antworten.
|
|
6
|
+
* Er traegt **keine** Kopfzeile `cashregister-token`: ein Partner arbeitet nie
|
|
7
|
+
* an einer Kasse, sondern ueber Betriebe.
|
|
8
|
+
*
|
|
9
|
+
* **Der Schluessel gehoert auf einen Server.** Er kann Betriebe anlegen und —
|
|
10
|
+
* mit `credentials:read` — deren Geheimnisse holen. In einer Browser- oder
|
|
11
|
+
* Mobil-App ist er ausgeliefert, nicht hinterlegt.
|
|
12
|
+
*/
|
|
13
|
+
import type { KasseneckAuth } from '../client/auth.js';
|
|
14
|
+
import type { PartnerEnv } from './typen.js';
|
|
15
|
+
/**
|
|
16
|
+
* Die Umgebung eines Partner-Schluessels, ohne Netzaufruf — `null`, wenn es
|
|
17
|
+
* keiner ist. Nuetzlich fuer die Zusicherung „auf diesem Server laeuft nur
|
|
18
|
+
* `pk_live_`" beim Hochfahren.
|
|
19
|
+
*/
|
|
20
|
+
export declare function partnerKeyEnv(schluessel: string): PartnerEnv | null;
|
|
21
|
+
export interface PartnerKeyAuthOptions {
|
|
22
|
+
/** Partner-Schluessel `pk_test_…` / `pk_live_…`. */
|
|
23
|
+
partnerKey: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Anmeldung per Partner-Schluessel.
|
|
27
|
+
*
|
|
28
|
+
* Die Form wird hier geprueft und nicht erst vom Server: ein vertauschter
|
|
29
|
+
* `kr_live_`-Schluessel (der eines Betriebs) faellt sonst als nichtssagendes
|
|
30
|
+
* „ungueltiger Schluessel" auf, obwohl er tadellos ist — nur eben fuer einen
|
|
31
|
+
* anderen Weg. Die Meldung nennt nie den Wert, nur seine Art.
|
|
32
|
+
*/
|
|
33
|
+
export declare function partnerKeyAuth(options: PartnerKeyAuthOptions): KasseneckAuth;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Anmeldung eines Partner-Servers: der Partner-Schluessel als Bearer.
|
|
4
|
+
*
|
|
5
|
+
* Ein dritter Weg neben `apiKeyAuth` (Geraete) und `registerUserAuth`
|
|
6
|
+
* (Browser-Kasse) — und der einzige, mit dem die Partner-Endpunkte antworten.
|
|
7
|
+
* Er traegt **keine** Kopfzeile `cashregister-token`: ein Partner arbeitet nie
|
|
8
|
+
* an einer Kasse, sondern ueber Betriebe.
|
|
9
|
+
*
|
|
10
|
+
* **Der Schluessel gehoert auf einen Server.** Er kann Betriebe anlegen und —
|
|
11
|
+
* mit `credentials:read` — deren Geheimnisse holen. In einer Browser- oder
|
|
12
|
+
* Mobil-App ist er ausgeliefert, nicht hinterlegt.
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.partnerKeyEnv = partnerKeyEnv;
|
|
16
|
+
exports.partnerKeyAuth = partnerKeyAuth;
|
|
17
|
+
const errors_js_1 = require("../client/errors.js");
|
|
18
|
+
/**
|
|
19
|
+
* Form eines Partner-Schluessels: `pk_test_…` bzw. `pk_live_…`. Der Rest ist
|
|
20
|
+
* opak — die Laenge kann sich aendern, das Praefix nicht (an ihm haengt die
|
|
21
|
+
* Umgebung).
|
|
22
|
+
*/
|
|
23
|
+
const SCHLUESSEL_FORM = /^pk_(test|live)_[A-Za-z0-9_-]{16,}$/;
|
|
24
|
+
/**
|
|
25
|
+
* Die Umgebung eines Partner-Schluessels, ohne Netzaufruf — `null`, wenn es
|
|
26
|
+
* keiner ist. Nuetzlich fuer die Zusicherung „auf diesem Server laeuft nur
|
|
27
|
+
* `pk_live_`" beim Hochfahren.
|
|
28
|
+
*/
|
|
29
|
+
function partnerKeyEnv(schluessel) {
|
|
30
|
+
const treffer = SCHLUESSEL_FORM.exec(typeof schluessel === 'string' ? schluessel.trim() : '');
|
|
31
|
+
return treffer ? treffer[1] : null;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Anmeldung per Partner-Schluessel.
|
|
35
|
+
*
|
|
36
|
+
* Die Form wird hier geprueft und nicht erst vom Server: ein vertauschter
|
|
37
|
+
* `kr_live_`-Schluessel (der eines Betriebs) faellt sonst als nichtssagendes
|
|
38
|
+
* „ungueltiger Schluessel" auf, obwohl er tadellos ist — nur eben fuer einen
|
|
39
|
+
* anderen Weg. Die Meldung nennt nie den Wert, nur seine Art.
|
|
40
|
+
*/
|
|
41
|
+
function partnerKeyAuth(options) {
|
|
42
|
+
const schluessel = typeof options?.partnerKey === 'string' ? options.partnerKey.trim() : '';
|
|
43
|
+
if (!schluessel) {
|
|
44
|
+
throw new errors_js_1.KasseneckAuthError('partnerKeyAuth: partnerKey fehlt');
|
|
45
|
+
}
|
|
46
|
+
if (!partnerKeyEnv(schluessel)) {
|
|
47
|
+
throw new errors_js_1.KasseneckAuthError('partnerKeyAuth: partnerKey hat nicht die Form pk_test_… / pk_live_… — ein Betriebsschluessel (kr_…) passt hier nicht');
|
|
48
|
+
}
|
|
49
|
+
// Pro Aufruf ein frisches Objekt: der Transport darf daran schreiben, ohne
|
|
50
|
+
// die naechste Anfrage zu vergiften (wie apiKeyAuth).
|
|
51
|
+
return () => ({ headers: { Authorization: `Bearer ${schluessel}` }, params: {} });
|
|
52
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Was ein Betrieb ueber die Schnittstelle mitbringen darf — Feld fuer Feld.
|
|
3
|
+
*
|
|
4
|
+
* **Unbekannte Felder werden abgewiesen, nicht stillschweigend verworfen.**
|
|
5
|
+
* Vorher verschwand ein `iban` oder ein vertippter Feldname spurlos: der
|
|
6
|
+
* Partner glaubte, er habe die Steuernummer geschickt, und Kasseneck hatte
|
|
7
|
+
* nichts. Ein Feld, das man schickt und das nichts bewirkt, ist der teuerste
|
|
8
|
+
* Fehler in einer Schnittstelle, weil ihn niemand bemerkt.
|
|
9
|
+
*
|
|
10
|
+
* Seitdem antwortet `createPartnerCustomer` mit `validation` und dem genauen
|
|
11
|
+
* Feldpfad — verschachtelt und je Kontakt: `address.land`,
|
|
12
|
+
* `tax_details.ustid`, `contacts.1.abteilung`.
|
|
13
|
+
*
|
|
14
|
+
* Zwei Dinge halten diese Seite dagegen:
|
|
15
|
+
*
|
|
16
|
+
* - der Typ [Betrieb] (typen.ts) hat genau diese Felder, und TypeScript meldet
|
|
17
|
+
* ein ueberzaehliges schon beim Tippen;
|
|
18
|
+
* - [unbekannteBetriebsfelder] beantwortet dieselbe Frage zur Laufzeit — fuer
|
|
19
|
+
* Daten, die aus einer Datenbank oder einem Formular kommen und deshalb nie
|
|
20
|
+
* durch die Typpruefung gelaufen sind.
|
|
21
|
+
*
|
|
22
|
+
* **Die Wahrheit bleibt der Server.** Dieser Client weist nichts von sich aus
|
|
23
|
+
* ab: eine spaetere Backend-Fassung darf ein Feld ergaenzen, ohne dass ein
|
|
24
|
+
* aelterer Client es blockiert. [unbekannteBetriebsfelder] ist die Vorschau,
|
|
25
|
+
* nicht das Tor.
|
|
26
|
+
*
|
|
27
|
+
* Quelle der Liste: `partner-core.BETRIEB_FELDER` im Backend.
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* Jedes erlaubte Feld als Pfad. `[]` markiert eine Liste — im Fehlerpfad des
|
|
31
|
+
* Servers steht dort der Index (`contacts.0.name`).
|
|
32
|
+
*
|
|
33
|
+
* Bewusst flach und nicht verschachtelt: so ist es EINE Liste, die der
|
|
34
|
+
* Zwilling Zeile fuer Zeile nachhalten kann. Das Schema fuer die Pruefung
|
|
35
|
+
* entsteht daraus (siehe unten) und nicht daneben.
|
|
36
|
+
*/
|
|
37
|
+
export declare const BETRIEB_FELDER: readonly ["companyName", "legalForm", "state", "industry", "companyRegister", "court", "web", "phone", "email", "billingEmail", "address.street", "address.number", "address.zip", "address.city", "taxDetails.taxNumber", "taxDetails.vatId", "taxDetails.gln", "taxDetails.smallBusiness", "contacts[].name", "contacts[].email", "contacts[].phone", "contacts[].roles", "taxAdvisor.name", "taxAdvisor.email", "taxAdvisor.phone", "taxAdvisor.mayContact"];
|
|
38
|
+
export type BetriebFeld = typeof BETRIEB_FELDER[number];
|
|
39
|
+
/**
|
|
40
|
+
* Die Feldpfade eines Betriebs, die [BETRIEB_FELDER] nicht kennt — dieselbe
|
|
41
|
+
* Ableitung wie im Backend, also dieselben Pfade wie in `data.errors[].field`.
|
|
42
|
+
* Leer heisst: aus dieser Sicht ist nichts ueberzaehlig.
|
|
43
|
+
*
|
|
44
|
+
* Sagt **nichts** ueber die Werte: Steuernummer, UID, PLZ und Gericht prueft
|
|
45
|
+
* das Backend mit `@kreiseck/validator`. Diese Funktion beantwortet nur die
|
|
46
|
+
* Frage „schicke ich etwas, das dort niemand erwartet?".
|
|
47
|
+
*/
|
|
48
|
+
export declare function unbekannteBetriebsfelder(business: unknown): string[];
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Was ein Betrieb ueber die Schnittstelle mitbringen darf — Feld fuer Feld.
|
|
4
|
+
*
|
|
5
|
+
* **Unbekannte Felder werden abgewiesen, nicht stillschweigend verworfen.**
|
|
6
|
+
* Vorher verschwand ein `iban` oder ein vertippter Feldname spurlos: der
|
|
7
|
+
* Partner glaubte, er habe die Steuernummer geschickt, und Kasseneck hatte
|
|
8
|
+
* nichts. Ein Feld, das man schickt und das nichts bewirkt, ist der teuerste
|
|
9
|
+
* Fehler in einer Schnittstelle, weil ihn niemand bemerkt.
|
|
10
|
+
*
|
|
11
|
+
* Seitdem antwortet `createPartnerCustomer` mit `validation` und dem genauen
|
|
12
|
+
* Feldpfad — verschachtelt und je Kontakt: `address.land`,
|
|
13
|
+
* `tax_details.ustid`, `contacts.1.abteilung`.
|
|
14
|
+
*
|
|
15
|
+
* Zwei Dinge halten diese Seite dagegen:
|
|
16
|
+
*
|
|
17
|
+
* - der Typ [Betrieb] (typen.ts) hat genau diese Felder, und TypeScript meldet
|
|
18
|
+
* ein ueberzaehliges schon beim Tippen;
|
|
19
|
+
* - [unbekannteBetriebsfelder] beantwortet dieselbe Frage zur Laufzeit — fuer
|
|
20
|
+
* Daten, die aus einer Datenbank oder einem Formular kommen und deshalb nie
|
|
21
|
+
* durch die Typpruefung gelaufen sind.
|
|
22
|
+
*
|
|
23
|
+
* **Die Wahrheit bleibt der Server.** Dieser Client weist nichts von sich aus
|
|
24
|
+
* ab: eine spaetere Backend-Fassung darf ein Feld ergaenzen, ohne dass ein
|
|
25
|
+
* aelterer Client es blockiert. [unbekannteBetriebsfelder] ist die Vorschau,
|
|
26
|
+
* nicht das Tor.
|
|
27
|
+
*
|
|
28
|
+
* Quelle der Liste: `partner-core.BETRIEB_FELDER` im Backend.
|
|
29
|
+
*/
|
|
30
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
|
+
exports.BETRIEB_FELDER = void 0;
|
|
32
|
+
exports.unbekannteBetriebsfelder = unbekannteBetriebsfelder;
|
|
33
|
+
/**
|
|
34
|
+
* Jedes erlaubte Feld als Pfad. `[]` markiert eine Liste — im Fehlerpfad des
|
|
35
|
+
* Servers steht dort der Index (`contacts.0.name`).
|
|
36
|
+
*
|
|
37
|
+
* Bewusst flach und nicht verschachtelt: so ist es EINE Liste, die der
|
|
38
|
+
* Zwilling Zeile fuer Zeile nachhalten kann. Das Schema fuer die Pruefung
|
|
39
|
+
* entsteht daraus (siehe unten) und nicht daneben.
|
|
40
|
+
*/
|
|
41
|
+
exports.BETRIEB_FELDER = [
|
|
42
|
+
'companyName',
|
|
43
|
+
'legalForm',
|
|
44
|
+
'state',
|
|
45
|
+
'industry',
|
|
46
|
+
'companyRegister',
|
|
47
|
+
'court',
|
|
48
|
+
'web',
|
|
49
|
+
'phone',
|
|
50
|
+
'email',
|
|
51
|
+
'billingEmail',
|
|
52
|
+
'address.street',
|
|
53
|
+
'address.number',
|
|
54
|
+
'address.zip',
|
|
55
|
+
'address.city',
|
|
56
|
+
'taxDetails.taxNumber',
|
|
57
|
+
'taxDetails.vatId',
|
|
58
|
+
'taxDetails.gln',
|
|
59
|
+
'taxDetails.smallBusiness',
|
|
60
|
+
'contacts[].name',
|
|
61
|
+
'contacts[].email',
|
|
62
|
+
'contacts[].phone',
|
|
63
|
+
'contacts[].roles',
|
|
64
|
+
'taxAdvisor.name',
|
|
65
|
+
'taxAdvisor.email',
|
|
66
|
+
'taxAdvisor.phone',
|
|
67
|
+
'taxAdvisor.mayContact',
|
|
68
|
+
];
|
|
69
|
+
/** Das Schema entsteht aus [BETRIEB_FELDER] — eine Quelle, keine zweite Liste. */
|
|
70
|
+
const SCHEMA = (() => {
|
|
71
|
+
const wurzel = {};
|
|
72
|
+
for (const pfad of exports.BETRIEB_FELDER) {
|
|
73
|
+
const teile = pfad.split('.');
|
|
74
|
+
let stand = wurzel;
|
|
75
|
+
teile.forEach((rohes, i) => {
|
|
76
|
+
const liste = rohes.endsWith('[]');
|
|
77
|
+
const name = liste ? rohes.slice(0, -2) : rohes;
|
|
78
|
+
if (i === teile.length - 1) {
|
|
79
|
+
stand[name] = true;
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (liste) {
|
|
83
|
+
const vorhanden = stand[name];
|
|
84
|
+
const eintrag = Array.isArray(vorhanden) ? vorhanden[0] : {};
|
|
85
|
+
stand[name] = [eintrag];
|
|
86
|
+
stand = eintrag;
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const vorhanden = stand[name];
|
|
90
|
+
const unter = vorhanden !== undefined && vorhanden !== true && !Array.isArray(vorhanden) ? vorhanden : {};
|
|
91
|
+
stand[name] = unter;
|
|
92
|
+
stand = unter;
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return wurzel;
|
|
96
|
+
})();
|
|
97
|
+
function istObjekt(wert) {
|
|
98
|
+
return wert !== null && typeof wert === 'object' && !Array.isArray(wert);
|
|
99
|
+
}
|
|
100
|
+
function sammle(eingabe, schema, pfad) {
|
|
101
|
+
if (!istObjekt(eingabe))
|
|
102
|
+
return [];
|
|
103
|
+
const raus = [];
|
|
104
|
+
for (const [name, wert] of Object.entries(eingabe)) {
|
|
105
|
+
const voll = pfad ? `${pfad}.${name}` : name;
|
|
106
|
+
const erlaubt = schema[name];
|
|
107
|
+
if (erlaubt === undefined) {
|
|
108
|
+
raus.push(voll);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (erlaubt === true)
|
|
112
|
+
continue;
|
|
113
|
+
if (Array.isArray(erlaubt)) {
|
|
114
|
+
// Ein falscher Typ ist kein unbekanntes Feld — den meldet der Server als
|
|
115
|
+
// eigenen Formfehler auf demselben Pfad.
|
|
116
|
+
if (!Array.isArray(wert))
|
|
117
|
+
continue;
|
|
118
|
+
wert.forEach((eintrag, idx) => raus.push(...sammle(eintrag, erlaubt[0], `${voll}.${idx}`)));
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
raus.push(...sammle(wert, erlaubt, voll));
|
|
122
|
+
}
|
|
123
|
+
return raus;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Die Feldpfade eines Betriebs, die [BETRIEB_FELDER] nicht kennt — dieselbe
|
|
127
|
+
* Ableitung wie im Backend, also dieselben Pfade wie in `data.errors[].field`.
|
|
128
|
+
* Leer heisst: aus dieser Sicht ist nichts ueberzaehlig.
|
|
129
|
+
*
|
|
130
|
+
* Sagt **nichts** ueber die Werte: Steuernummer, UID, PLZ und Gericht prueft
|
|
131
|
+
* das Backend mit `@kreiseck/validator`. Diese Funktion beantwortet nur die
|
|
132
|
+
* Frage „schicke ich etwas, das dort niemand erwartet?".
|
|
133
|
+
*/
|
|
134
|
+
function unbekannteBetriebsfelder(business) {
|
|
135
|
+
return sammle(business, SCHEMA, '');
|
|
136
|
+
}
|