@kreiseck/kasseneck-api 1.0.0-rc.1 → 1.0.0-rc.2
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 +22 -7
- package/README.md +33 -10
- package/dist/cjs/client/errors.d.ts +26 -10
- package/dist/cjs/client/errors.js +35 -10
- package/dist/cjs/client/receipts.js +3 -21
- package/dist/cjs/client/transport.js +25 -13
- package/dist/cjs/payments/hobex.d.ts +9 -1
- package/dist/cjs/payments/hobex.js +14 -2
- package/dist/cjs/payments/stripe.d.ts +3 -0
- package/dist/cjs/payments/stripe.js +6 -1
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/client/errors.d.ts +26 -10
- package/dist/esm/client/errors.js +34 -10
- package/dist/esm/client/receipts.js +1 -19
- package/dist/esm/client/transport.js +25 -13
- package/dist/esm/payments/hobex.d.ts +9 -1
- package/dist/esm/payments/hobex.js +15 -3
- package/dist/esm/payments/stripe.d.ts +3 -0
- package/dist/esm/payments/stripe.js +7 -2
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/fixtures/hobex-hps-codes.json +1 -1
- package/fixtures/invoice-api.schema.json +1 -1
- package/fixtures/invoice-texts.json +1 -1
- package/fixtures/pos-texts.json +1 -1
- package/fixtures/surface.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -104,13 +104,28 @@ an upgrade as long as `/v1` is served.
|
|
|
104
104
|
means the operation may have happened: `dialect_mismatch`,
|
|
105
105
|
`receipt_outcome_unknown`, `cancellation_outcome_unknown`,
|
|
106
106
|
`response_translation_failed` (unless `details.handled === false`),
|
|
107
|
-
`response_unreadable` (a signing call reported success but the
|
|
108
|
-
lacks the receipt, the reference
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
107
|
+
`response_unreadable` (a signing or money call reported success but the
|
|
108
|
+
response lacks the receipt, the reference, the remaining quantities, the
|
|
109
|
+
Hobex receipt or the captured payment intent), and on `createReceipt`,
|
|
110
|
+
`cancelReceipt`, `financeWebService`, `hobexPayApi`, `hobexRefundApi` and
|
|
111
|
+
`stripeCaptureIntent` a network error, timeout or HTTP 5xx after sending as
|
|
112
|
+
well as HTTP 200 with the `/v3` marker but an empty, non-JSON (also HTML) or
|
|
113
|
+
status-less body. Never retry those; read the result back. Reason: a
|
|
114
|
+
retried receipt is a second signed receipt in the chain.
|
|
115
|
+
- **Money calls have an unknown outcome too (1.0.0-rc.2).** `hobexPay`,
|
|
116
|
+
`hobexRefund` and `stripeCaptureIntent` joined the calls above. In rc.1 a
|
|
117
|
+
timeout, network error or HTTP 5xx on them was `'rejected'`, and a success
|
|
118
|
+
reply without a readable Hobex receipt or payment intent was a
|
|
119
|
+
`KasseneckValidationError` (`scope: 'response'`); now both are `'unknown'`
|
|
120
|
+
(the latter as `KasseneckApiError` `response_unreadable`), and HTML with the
|
|
121
|
+
`/v3` marker is `not-json` with `'unknown'` instead of `route_missing`. An
|
|
122
|
+
error envelope throws a `KasseneckApiError` with its `code`; `hobexRefund`
|
|
123
|
+
never returns `false`. **Callers must never retry these calls, and must not
|
|
124
|
+
pass a `fetch` that retries by itself or wrap the package's requests in a
|
|
125
|
+
retrying layer** (proxy, service worker, HTTP client with a retry policy).
|
|
126
|
+
Reason: an app that read `'rejected'` could charge or refund a card twice;
|
|
127
|
+
a silent resend below the package does the same without any error. Same
|
|
128
|
+
change as in the Dart twin `kasseneck_api` (no `RetryClient`).
|
|
114
129
|
- **Strict validation before sending.** The 0.x payment fields
|
|
115
130
|
(`paymentMethod`, `paymentMethodFromServer`, `creditCardProvider`,
|
|
116
131
|
`cardPaymentId`, `cardPaymentData`) throw, also from plain JavaScript.
|
package/README.md
CHANGED
|
@@ -427,9 +427,10 @@ silently. For a layout option of 0.x the message names its English successor.
|
|
|
427
427
|
### `outcome: 'unknown'`: never retry, look it up
|
|
428
428
|
|
|
429
429
|
`KasseneckApiError`, `KasseneckHttpError` and `KasseneckNetworkError` carry
|
|
430
|
-
`outcome`. `'rejected'` means no
|
|
431
|
-
signed
|
|
432
|
-
|
|
430
|
+
`outcome`. `'rejected'` means no operation with an effect is left open: no
|
|
431
|
+
receipt was signed, nothing went to FinanzOnline and no money moved, so
|
|
432
|
+
retrying the same request does not create a second receipt or charge (whether
|
|
433
|
+
it helps depends on the code). `'unknown'` means the operation **may have been carried out**, for
|
|
433
434
|
`createReceipt` a signed receipt in the chain. Then never send it again: read
|
|
434
435
|
the result back (`getReceipt`, `listMyReceipts`, the original of a
|
|
435
436
|
cancellation) and continue from there. `isOutcomeUnknown(error)` covers all
|
|
@@ -437,18 +438,35 @@ three classes. The outcome is unknown for:
|
|
|
437
438
|
|
|
438
439
|
- `dialect_mismatch`, `receipt_outcome_unknown`, `cancellation_outcome_unknown`;
|
|
439
440
|
- `response_translation_failed`, unless `details.handled === false`;
|
|
440
|
-
- `response_unreadable`: a signing call reported success,
|
|
441
|
-
lacks what the call promises (no receipt, no reference, no
|
|
442
|
-
quantities);
|
|
443
|
-
- on the signing calls `createReceipt`,
|
|
444
|
-
`
|
|
441
|
+
- `response_unreadable`: a signing or money-moving call reported success,
|
|
442
|
+
but the response lacks what the call promises (no receipt, no reference, no
|
|
443
|
+
remaining quantities, no Hobex receipt, no captured payment intent);
|
|
444
|
+
- on the calls with an effect: the signing calls `createReceipt`,
|
|
445
|
+
`cancelReceipt` and `financeWebService`, and the money calls `hobexPay`
|
|
446
|
+
(`hobexPayApi`, charges a card), `hobexRefund` (`hobexRefundApi`) and
|
|
447
|
+
`stripeCaptureIntent`: a network error or timeout after sending began, HTTP
|
|
445
448
|
5xx, and HTTP 200 with the `Kasseneck-Api-Version: v3` marker but an empty,
|
|
446
449
|
non-JSON (also `text/html`) or status-less body (`KasseneckHttpError`,
|
|
447
450
|
`reason` `empty-body`, `not-json` or `missing-status`). HTML without the
|
|
448
451
|
marker stays `route_missing` with `'rejected'`: no function saw the call.
|
|
449
452
|
|
|
450
|
-
|
|
451
|
-
|
|
453
|
+
On the money calls an unknown outcome means the card may have been charged,
|
|
454
|
+
the refund may have gone through, the payment may have been captured. A
|
|
455
|
+
rejection of `hobexRefund` throws a `KasseneckApiError` with its `code`; it
|
|
456
|
+
never returns `false`, because a `false` on an unclear outcome invites a
|
|
457
|
+
second refund.
|
|
458
|
+
|
|
459
|
+
**Never retry these six calls, and never put a retrying layer under the
|
|
460
|
+
package.** A `fetch` passed in `options.fetch` (or a proxy or service worker in
|
|
461
|
+
front of it) must not resend a request by itself after a network error, a
|
|
462
|
+
timeout or a 5xx: a silent second send is a second signed receipt, a second
|
|
463
|
+
charge or a second refund, and the package cannot see it. Look the result up
|
|
464
|
+
instead (`getReceipt`, `listMyReceipts`, the Hobex transaction by its
|
|
465
|
+
`transactionId`, the Stripe session).
|
|
466
|
+
|
|
467
|
+
`outcome` only covers signing and the money calls. After a network error on
|
|
468
|
+
`issueInvoice` an invoice may still have been issued; retry it with the same
|
|
469
|
+
`idempotencyKey`.
|
|
452
470
|
|
|
453
471
|
```ts
|
|
454
472
|
import { isOutcomeUnknown, paymentsExpectedCents } from '@kreiseck/kasseneck-api';
|
|
@@ -792,6 +810,11 @@ process:
|
|
|
792
810
|
- **Hobex cloud** (`hobexPay`, `hobexRefund`): a terminal registered with
|
|
793
811
|
Hobex, controlled over the network through the backend. Amounts are passed in
|
|
794
812
|
cents; the package converts to euros for Hobex.
|
|
813
|
+
|
|
814
|
+
`hobexPay`, `hobexRefund` and `stripeCaptureIntent` move money. They are never
|
|
815
|
+
retried, neither by the app nor by a retrying `fetch` under the package: on
|
|
816
|
+
`isOutcomeUnknown(error)` look the payment up (see
|
|
817
|
+
[`outcome: 'unknown'`](#outcome-unknown-never-retry-look-it-up)).
|
|
795
818
|
- **Hobex HPS via Kasseneck Connect**, described below.
|
|
796
819
|
|
|
797
820
|
### Hobex HPS via Kasseneck Connect
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* Single-Page-App hat geantwortet, der Aufruf kam nie an),
|
|
21
21
|
* `dialect_mismatch` (Antwort ohne Kennzeichen `Kasseneck-Api-Version: v3`:
|
|
22
22
|
* ein Rand ohne `/v3` hat geantwortet, **Ausgang unklar**) und
|
|
23
|
-
* `response_unreadable` (ein
|
|
23
|
+
* `response_unreadable` (ein Aufruf mit Wirkung meldete Erfolg, die
|
|
24
24
|
* Antwort traegt aber nicht, was er zusagt: **Ausgang unklar**).
|
|
25
25
|
* **Entscheidend ist `outcome`:** `'rejected'` heisst abgelehnt, nichts
|
|
26
26
|
* geschehen, Wiederholen hilft nicht. `'unknown'` heisst: der Vorgang kann
|
|
@@ -29,14 +29,16 @@
|
|
|
29
29
|
* - `KasseneckHttpError` — die Antwort war **keine** verwertbare Huelle:
|
|
30
30
|
* HTTP 500/404 ohne Huelle, leerer Rumpf oder Text statt JSON. Beim
|
|
31
31
|
* Bericht-Download gelten dieselben Gruende fuer alles, was kein PDF ist.
|
|
32
|
-
* `reason` trennt die Faelle maschinenlesbar. Auf einem
|
|
33
|
-
*
|
|
34
|
-
* Kennzeichen, aber leerem oder
|
|
32
|
+
* `reason` trennt die Faelle maschinenlesbar. Auf einem Aufruf mit
|
|
33
|
+
* Wirkung (signierend oder geldbewegend) hat HTTP 5xx
|
|
34
|
+
* `outcome: 'unknown'`, ebenso HTTP 200 mit Kennzeichen, aber leerem oder
|
|
35
|
+
* unlesbarem Rumpf.
|
|
35
36
|
* - `KasseneckNetworkError` — die Antwort kam gar nicht: Netz weg, DNS,
|
|
36
37
|
* abgebrochene Verbindung oder Zeitueberschreitung (`timedOut`). Auch hier
|
|
37
38
|
* gilt `outcome`: war die Anfrage schon unterwegs und ist der Aufruf einer
|
|
38
|
-
*
|
|
39
|
-
*
|
|
39
|
+
* mit Wirkung (signierend: `createReceipt`, `cancelReceipt`,
|
|
40
|
+
* `financeWebService`; geldbewegend: `hobexPayApi`, `hobexRefundApi`,
|
|
41
|
+
* `stripeCaptureIntent`), ist er `'unknown'`.
|
|
40
42
|
* - `KasseneckAuthError` — es kam nicht einmal zur Anfrage, weil die Anmeldung
|
|
41
43
|
* scheiterte (fehlende Zugangsdaten, oder der Token-/Sitzungsgeber warf).
|
|
42
44
|
* In der Browser-Kasse mit ihrer 90-Sekunden-Sitzung ist das Alltag, kein
|
|
@@ -111,7 +113,7 @@ export type ErrorOutcome = 'unknown' | 'rejected';
|
|
|
111
113
|
/**
|
|
112
114
|
* Codes, die das Paket selbst vergibt, nicht der Server: `route_missing`
|
|
113
115
|
* (HTML statt Backend, der Aufruf kam nie an) und `response_unreadable`
|
|
114
|
-
* (ein
|
|
116
|
+
* (ein Aufruf mit Wirkung meldete Erfolg, die Antwort ist aber unlesbar;
|
|
115
117
|
* Ausgang unklar). `dialect_mismatch` vergibt das Paket ebenfalls, der Code
|
|
116
118
|
* gehoert aber schon zum Rand des Servers (`errorCodes.edge`).
|
|
117
119
|
*/
|
|
@@ -142,7 +144,7 @@ export declare class KasseneckApiError extends Error {
|
|
|
142
144
|
/**
|
|
143
145
|
* `'unknown'` bei `dialect_mismatch`, `receipt_outcome_unknown`,
|
|
144
146
|
* `cancellation_outcome_unknown`, `response_unreadable` (Erfolg gemeldet,
|
|
145
|
-
* Antwort eines
|
|
147
|
+
* Antwort eines Aufrufs mit Wirkung aber unlesbar) und
|
|
146
148
|
* `response_translation_failed` (ausser mit `details.handled === false`);
|
|
147
149
|
* sonst `'rejected'`. Bei `'unknown'` nie wiederholen, sondern nachlesen.
|
|
148
150
|
*/
|
|
@@ -162,8 +164,9 @@ export declare class KasseneckHttpError extends Error {
|
|
|
162
164
|
/** Maschinenlesbarer Grund — trennt den Rewrite-Fall vom 500er ohne Textparsen. */
|
|
163
165
|
readonly reason: HttpFailureReason;
|
|
164
166
|
/**
|
|
165
|
-
* `'unknown'` auf einem
|
|
166
|
-
* `cancelReceipt`, `financeWebService`
|
|
167
|
+
* `'unknown'` auf einem Aufruf mit Wirkung (`createReceipt`,
|
|
168
|
+
* `cancelReceipt`, `financeWebService`, `hobexPayApi`, `hobexRefundApi`,
|
|
169
|
+
* `stripeCaptureIntent`) bei HTTP 5xx und bei HTTP 200 mit
|
|
167
170
|
* Kennzeichen, aber unlesbarem Rumpf (`empty-body`, `not-json` auch bei
|
|
168
171
|
* `text/html`, `missing-status`): der Handler kann gelaufen sein, nie wiederholen,
|
|
169
172
|
* sondern nachlesen. Sonst `'rejected'` (auch 4xx).
|
|
@@ -249,3 +252,16 @@ export declare function isKasseneckHttpError(error: unknown): error is Kasseneck
|
|
|
249
252
|
export declare function isKasseneckNetworkError(error: unknown): error is KasseneckNetworkError;
|
|
250
253
|
export declare function isKasseneckAuthError(error: unknown): error is KasseneckAuthError;
|
|
251
254
|
export declare function isKasseneckValidationError(error: unknown): error is KasseneckValidationError;
|
|
255
|
+
/**
|
|
256
|
+
* Liest die Erfolgsantwort eines Aufrufs **mit Wirkung** (signierend oder
|
|
257
|
+
* geldbewegend). Scheitert das Lesen (fehlender Beleg, fehlender Bezug,
|
|
258
|
+
* unbrauchbares Feld oder ein Laufzeitfehler beim Umwandeln), hat der Server
|
|
259
|
+
* trotzdem Erfolg gemeldet: der Beleg ist signiert und im DEP, die Karte
|
|
260
|
+
* belastet bzw. der Einzug gelaufen. Das darf nie als gewoehnlicher Fehler
|
|
261
|
+
* enden, sonst kassiert die Kasse ein zweites Mal. Darum wird daraus
|
|
262
|
+
* `KasseneckApiError` mit Code `response_unreadable` und `outcome: 'unknown'`.
|
|
263
|
+
* Der Grund stammt vom Paket; aus der Antwort selbst wird nichts uebernommen.
|
|
264
|
+
*
|
|
265
|
+
* Paketintern (receipts.ts, payments/); nicht Teil der Paketoberflaeche.
|
|
266
|
+
*/
|
|
267
|
+
export declare function signiertGelesen<T>(functionName: string, lesen: () => T): T;
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* Single-Page-App hat geantwortet, der Aufruf kam nie an),
|
|
22
22
|
* `dialect_mismatch` (Antwort ohne Kennzeichen `Kasseneck-Api-Version: v3`:
|
|
23
23
|
* ein Rand ohne `/v3` hat geantwortet, **Ausgang unklar**) und
|
|
24
|
-
* `response_unreadable` (ein
|
|
24
|
+
* `response_unreadable` (ein Aufruf mit Wirkung meldete Erfolg, die
|
|
25
25
|
* Antwort traegt aber nicht, was er zusagt: **Ausgang unklar**).
|
|
26
26
|
* **Entscheidend ist `outcome`:** `'rejected'` heisst abgelehnt, nichts
|
|
27
27
|
* geschehen, Wiederholen hilft nicht. `'unknown'` heisst: der Vorgang kann
|
|
@@ -30,14 +30,16 @@
|
|
|
30
30
|
* - `KasseneckHttpError` — die Antwort war **keine** verwertbare Huelle:
|
|
31
31
|
* HTTP 500/404 ohne Huelle, leerer Rumpf oder Text statt JSON. Beim
|
|
32
32
|
* Bericht-Download gelten dieselben Gruende fuer alles, was kein PDF ist.
|
|
33
|
-
* `reason` trennt die Faelle maschinenlesbar. Auf einem
|
|
34
|
-
*
|
|
35
|
-
* Kennzeichen, aber leerem oder
|
|
33
|
+
* `reason` trennt die Faelle maschinenlesbar. Auf einem Aufruf mit
|
|
34
|
+
* Wirkung (signierend oder geldbewegend) hat HTTP 5xx
|
|
35
|
+
* `outcome: 'unknown'`, ebenso HTTP 200 mit Kennzeichen, aber leerem oder
|
|
36
|
+
* unlesbarem Rumpf.
|
|
36
37
|
* - `KasseneckNetworkError` — die Antwort kam gar nicht: Netz weg, DNS,
|
|
37
38
|
* abgebrochene Verbindung oder Zeitueberschreitung (`timedOut`). Auch hier
|
|
38
39
|
* gilt `outcome`: war die Anfrage schon unterwegs und ist der Aufruf einer
|
|
39
|
-
*
|
|
40
|
-
*
|
|
40
|
+
* mit Wirkung (signierend: `createReceipt`, `cancelReceipt`,
|
|
41
|
+
* `financeWebService`; geldbewegend: `hobexPayApi`, `hobexRefundApi`,
|
|
42
|
+
* `stripeCaptureIntent`), ist er `'unknown'`.
|
|
41
43
|
* - `KasseneckAuthError` — es kam nicht einmal zur Anfrage, weil die Anmeldung
|
|
42
44
|
* scheiterte (fehlende Zugangsdaten, oder der Token-/Sitzungsgeber warf).
|
|
43
45
|
* In der Browser-Kasse mit ihrer 90-Sekunden-Sitzung ist das Alltag, kein
|
|
@@ -80,6 +82,7 @@ exports.isKasseneckHttpError = isKasseneckHttpError;
|
|
|
80
82
|
exports.isKasseneckNetworkError = isKasseneckNetworkError;
|
|
81
83
|
exports.isKasseneckAuthError = isKasseneckAuthError;
|
|
82
84
|
exports.isKasseneckValidationError = isKasseneckValidationError;
|
|
85
|
+
exports.signiertGelesen = signiertGelesen;
|
|
83
86
|
// Bezeichner-artig: Buchstabe vorn, danach nur Bezeichnerzeichen, hoechstens
|
|
84
87
|
// 64 Zeichen. Das laesst `TypeError`, `ECONNREFUSED` und `auth/internal-error`
|
|
85
88
|
// durch, aber keinen Freitext und kein ID-Token (~900 Zeichen).
|
|
@@ -236,7 +239,7 @@ function ausgangAusCode(code, details) {
|
|
|
236
239
|
/**
|
|
237
240
|
* Codes, die das Paket selbst vergibt, nicht der Server: `route_missing`
|
|
238
241
|
* (HTML statt Backend, der Aufruf kam nie an) und `response_unreadable`
|
|
239
|
-
* (ein
|
|
242
|
+
* (ein Aufruf mit Wirkung meldete Erfolg, die Antwort ist aber unlesbar;
|
|
240
243
|
* Ausgang unklar). `dialect_mismatch` vergibt das Paket ebenfalls, der Code
|
|
241
244
|
* gehoert aber schon zum Rand des Servers (`errorCodes.edge`).
|
|
242
245
|
*/
|
|
@@ -266,7 +269,7 @@ class KasseneckApiError extends Error {
|
|
|
266
269
|
/**
|
|
267
270
|
* `'unknown'` bei `dialect_mismatch`, `receipt_outcome_unknown`,
|
|
268
271
|
* `cancellation_outcome_unknown`, `response_unreadable` (Erfolg gemeldet,
|
|
269
|
-
* Antwort eines
|
|
272
|
+
* Antwort eines Aufrufs mit Wirkung aber unlesbar) und
|
|
270
273
|
* `response_translation_failed` (ausser mit `details.handled === false`);
|
|
271
274
|
* sonst `'rejected'`. Bei `'unknown'` nie wiederholen, sondern nachlesen.
|
|
272
275
|
*/
|
|
@@ -301,8 +304,9 @@ class KasseneckHttpError extends Error {
|
|
|
301
304
|
/** Maschinenlesbarer Grund — trennt den Rewrite-Fall vom 500er ohne Textparsen. */
|
|
302
305
|
reason;
|
|
303
306
|
/**
|
|
304
|
-
* `'unknown'` auf einem
|
|
305
|
-
* `cancelReceipt`, `financeWebService`
|
|
307
|
+
* `'unknown'` auf einem Aufruf mit Wirkung (`createReceipt`,
|
|
308
|
+
* `cancelReceipt`, `financeWebService`, `hobexPayApi`, `hobexRefundApi`,
|
|
309
|
+
* `stripeCaptureIntent`) bei HTTP 5xx und bei HTTP 200 mit
|
|
306
310
|
* Kennzeichen, aber unlesbarem Rumpf (`empty-body`, `not-json` auch bei
|
|
307
311
|
* `text/html`, `missing-status`): der Handler kann gelaufen sein, nie wiederholen,
|
|
308
312
|
* sondern nachlesen. Sonst `'rejected'` (auch 4xx).
|
|
@@ -426,3 +430,24 @@ function isKasseneckAuthError(error) {
|
|
|
426
430
|
function isKasseneckValidationError(error) {
|
|
427
431
|
return error instanceof KasseneckValidationError;
|
|
428
432
|
}
|
|
433
|
+
/**
|
|
434
|
+
* Liest die Erfolgsantwort eines Aufrufs **mit Wirkung** (signierend oder
|
|
435
|
+
* geldbewegend). Scheitert das Lesen (fehlender Beleg, fehlender Bezug,
|
|
436
|
+
* unbrauchbares Feld oder ein Laufzeitfehler beim Umwandeln), hat der Server
|
|
437
|
+
* trotzdem Erfolg gemeldet: der Beleg ist signiert und im DEP, die Karte
|
|
438
|
+
* belastet bzw. der Einzug gelaufen. Das darf nie als gewoehnlicher Fehler
|
|
439
|
+
* enden, sonst kassiert die Kasse ein zweites Mal. Darum wird daraus
|
|
440
|
+
* `KasseneckApiError` mit Code `response_unreadable` und `outcome: 'unknown'`.
|
|
441
|
+
* Der Grund stammt vom Paket; aus der Antwort selbst wird nichts uebernommen.
|
|
442
|
+
*
|
|
443
|
+
* Paketintern (receipts.ts, payments/); nicht Teil der Paketoberflaeche.
|
|
444
|
+
*/
|
|
445
|
+
function signiertGelesen(functionName, lesen) {
|
|
446
|
+
try {
|
|
447
|
+
return lesen();
|
|
448
|
+
}
|
|
449
|
+
catch (ursache) {
|
|
450
|
+
const grund = ursache instanceof KasseneckValidationError ? ursache.reason : 'Antwort nicht lesbar';
|
|
451
|
+
throw new KasseneckApiError(functionName, `Erfolg gemeldet, Antwort aber unlesbar (${grund}). Der Vorgang kann ausgefuehrt sein: nicht wiederholen, sondern nachlesen.`, {}, 'response_unreadable');
|
|
452
|
+
}
|
|
453
|
+
}
|
|
@@ -58,12 +58,12 @@ function receiptLayoutFromResult(result, options = {}) {
|
|
|
58
58
|
*/
|
|
59
59
|
async function createReceipt(rufen, options) {
|
|
60
60
|
const daten = await rufen('createReceipt', createReceiptParams(options));
|
|
61
|
-
return signiertGelesen('createReceipt', () => belegAusHuelle(daten, 'createReceipt'));
|
|
61
|
+
return (0, errors_js_1.signiertGelesen)('createReceipt', () => belegAusHuelle(daten, 'createReceipt'));
|
|
62
62
|
}
|
|
63
63
|
/** Wie [createReceipt], liest aus derselben Antwort zusaetzlich die Firmendaten. */
|
|
64
64
|
async function createReceiptWithCompany(rufen, options) {
|
|
65
65
|
const daten = await rufen('createReceipt', createReceiptParams(options));
|
|
66
|
-
return signiertGelesen('createReceipt', () => belegMitFirmaAusHuelle(daten, 'createReceipt'));
|
|
66
|
+
return (0, errors_js_1.signiertGelesen)('createReceipt', () => belegMitFirmaAusHuelle(daten, 'createReceipt'));
|
|
67
67
|
}
|
|
68
68
|
/**
|
|
69
69
|
* Baut die Nutzlast von `createReceipt` und prueft die Eingabe. Wirft, bevor
|
|
@@ -199,7 +199,7 @@ async function cancelReceipt(transport, options) {
|
|
|
199
199
|
if (zahlungen !== undefined)
|
|
200
200
|
params.payments = zahlungen;
|
|
201
201
|
const daten = await transport('cancelReceipt', params);
|
|
202
|
-
return signiertGelesen('cancelReceipt', () => stornoAusHuelle(daten));
|
|
202
|
+
return (0, errors_js_1.signiertGelesen)('cancelReceipt', () => stornoAusHuelle(daten));
|
|
203
203
|
}
|
|
204
204
|
/** Liest die Storno-Antwort `{ receipt, cancellationOf, remaining }`. */
|
|
205
205
|
function stornoAusHuelle(daten) {
|
|
@@ -685,24 +685,6 @@ function kartenanbieter(wert) {
|
|
|
685
685
|
function eingabefehler(grund) {
|
|
686
686
|
return new errors_js_1.KasseneckValidationError('createReceipt', grund, 'request');
|
|
687
687
|
}
|
|
688
|
-
/**
|
|
689
|
-
* Liest die Erfolgsantwort eines **signierenden** Aufrufs. Scheitert das
|
|
690
|
-
* Lesen (fehlender Beleg, fehlender Bezug, unbrauchbares Feld oder ein
|
|
691
|
-
* Laufzeitfehler beim Umwandeln), hat der Server trotzdem Erfolg gemeldet:
|
|
692
|
-
* der Beleg ist signiert und im DEP. Das darf nie als gewoehnlicher Fehler
|
|
693
|
-
* enden, sonst kassiert die Kasse ein zweites Mal. Darum wird daraus
|
|
694
|
-
* `KasseneckApiError` mit Code `response_unreadable` und `outcome: 'unknown'`.
|
|
695
|
-
* Der Grund stammt vom Paket; aus der Antwort selbst wird nichts uebernommen.
|
|
696
|
-
*/
|
|
697
|
-
function signiertGelesen(functionName, lesen) {
|
|
698
|
-
try {
|
|
699
|
-
return lesen();
|
|
700
|
-
}
|
|
701
|
-
catch (ursache) {
|
|
702
|
-
const grund = ursache instanceof errors_js_1.KasseneckValidationError ? ursache.reason : 'Antwort nicht lesbar';
|
|
703
|
-
throw new errors_js_1.KasseneckApiError(functionName, `Erfolg gemeldet, Antwort aber unlesbar (${grund}). Der Vorgang kann ausgefuehrt sein: nicht wiederholen, sondern nachlesen.`, {}, 'response_unreadable');
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
688
|
/** Die Antwort meldete Erfolg, trug aber nicht, was der Aufruf zusagt. */
|
|
707
689
|
function antwortfehler(functionName, grund) {
|
|
708
690
|
return new errors_js_1.KasseneckValidationError(functionName, grund, 'response');
|
|
@@ -62,10 +62,22 @@ const KASSENECK_HOSTS = new Set(['api.kasseneck.at', 'kasse.kasseneck.at']);
|
|
|
62
62
|
/** Die 1.x-Linie spricht nur `/v3`: jede Basis endet so (`/v3` oder `/api/v3`). */
|
|
63
63
|
const V3_ENDE = /\/v3$/;
|
|
64
64
|
/**
|
|
65
|
-
* Aufrufe, die
|
|
66
|
-
*
|
|
65
|
+
* Aufrufe mit Wirkung, die nie blind wiederholt werden duerfen: sie signieren
|
|
66
|
+
* (`createReceipt`, `cancelReceipt`), loesen bei FinanzOnline etwas aus
|
|
67
|
+
* (`financeWebService`) oder bewegen Geld (`hobexPayApi` belastet eine Karte,
|
|
68
|
+
* `hobexRefundApi` erstattet, `stripeCaptureIntent` zieht eine vorgemerkte
|
|
69
|
+
* Zahlung ein). Scheitert einer, nachdem die Anfrage unterwegs war (Netz,
|
|
70
|
+
* Zeitlimit, HTTP 5xx, unlesbare Erfolgsantwort, HTML mit Kennzeichen), ist
|
|
71
|
+
* sein Ausgang offen.
|
|
67
72
|
*/
|
|
68
|
-
const
|
|
73
|
+
const UNKNOWN_OUTCOME_CALLS = new Set([
|
|
74
|
+
'createReceipt',
|
|
75
|
+
'cancelReceipt',
|
|
76
|
+
'financeWebService',
|
|
77
|
+
'hobexPayApi',
|
|
78
|
+
'hobexRefundApi',
|
|
79
|
+
'stripeCaptureIntent',
|
|
80
|
+
]);
|
|
69
81
|
/**
|
|
70
82
|
* Produkte, die das Backend in `Kasseneck-Client` zaehlt (Positivliste,
|
|
71
83
|
* Nachtrag §6); alles andere zaehlte dort als `ungueltig`. Das Paket weist
|
|
@@ -162,9 +174,9 @@ function createCore(options) {
|
|
|
162
174
|
return ursache;
|
|
163
175
|
}
|
|
164
176
|
// Bis hierher kam keine verwertbare Antwort: Netz weg oder Zeitlimit.
|
|
165
|
-
// Die Anfrage war schon unterwegs: bei einem
|
|
166
|
-
// der Beleg entstanden sein (Ausgang unklar, nachlesen).
|
|
167
|
-
return new errors_js_1.KasseneckNetworkError(fehlerName, abbruch.signal.aborted, zeitlimitMs, (0, errors_js_1.causeDigest)(ursache, geheimnisse),
|
|
177
|
+
// Die Anfrage war schon unterwegs: bei einem Aufruf mit Wirkung kann
|
|
178
|
+
// der Beleg bzw. die Zahlung entstanden sein (Ausgang unklar, nachlesen).
|
|
179
|
+
return new errors_js_1.KasseneckNetworkError(fehlerName, abbruch.signal.aborted, zeitlimitMs, (0, errors_js_1.causeDigest)(ursache, geheimnisse), UNKNOWN_OUTCOME_CALLS.has(functionName) ? 'unknown' : 'rejected');
|
|
168
180
|
};
|
|
169
181
|
const basis = basisFuer(functionName);
|
|
170
182
|
const url = `${basis}/${encodeURIComponent(functionName)}`;
|
|
@@ -217,18 +229,18 @@ function createCore(options) {
|
|
|
217
229
|
if (fehler)
|
|
218
230
|
throw fehler;
|
|
219
231
|
}
|
|
220
|
-
// 5xx auf einem
|
|
221
|
-
const ausgang = antwort.status >= 500 &&
|
|
232
|
+
// 5xx auf einem Aufruf mit Wirkung: der Handler kann gelaufen sein.
|
|
233
|
+
const ausgang = antwort.status >= 500 && UNKNOWN_OUTCOME_CALLS.has(functionName) ? 'unknown' : 'rejected';
|
|
222
234
|
throw new errors_js_1.KasseneckHttpError(fehlerName, antwort.status, inhaltstyp, 'server-error', ausgang);
|
|
223
235
|
}
|
|
224
236
|
// HTTP 200 mit HTML: die Auffangregel der Single-Page-App hat den Aufruf
|
|
225
237
|
// bedient, keine Function hat ihn gesehen (Nachtrag §5.4, R15).
|
|
226
238
|
if (inhaltstyp !== undefined && /^\s*text\/html\b/i.test(inhaltstyp)) {
|
|
227
239
|
// Mit Kennzeichen hat der `/v3`-Rand den Aufruf gesehen (etwa ein
|
|
228
|
-
// Proxy, der nur den Inhaltstyp umschreibt). Bei einem
|
|
229
|
-
//
|
|
240
|
+
// Proxy, der nur den Inhaltstyp umschreibt). Bei einem Aufruf mit
|
|
241
|
+
// Wirkung kann der Beleg bzw. die Zahlung dann entstanden sein: unlesbarer Rumpf,
|
|
230
242
|
// Ausgang unklar, statt `route_missing`.
|
|
231
|
-
if (
|
|
243
|
+
if (UNKNOWN_OUTCOME_CALLS.has(functionName) && traegtKennzeichen(antwort)) {
|
|
232
244
|
throw new errors_js_1.KasseneckHttpError(fehlerName, antwort.status, inhaltstyp, 'not-json', 'unknown');
|
|
233
245
|
}
|
|
234
246
|
throw new errors_js_1.KasseneckApiError(fehlerName, 'Route fehlt: die Antwort ist eine HTML-Seite statt des Backends', {}, 'route_missing');
|
|
@@ -245,8 +257,8 @@ function createCore(options) {
|
|
|
245
257
|
}
|
|
246
258
|
// Ab hier kam HTTP 200 mit Kennzeichen: der `/v3`-Rand hat den Aufruf
|
|
247
259
|
// gesehen. Ist der Rumpf dann unlesbar (gekuerzt von einem Proxy,
|
|
248
|
-
// abgebrochene Verbindung), kann ein
|
|
249
|
-
const unlesbar =
|
|
260
|
+
// abgebrochene Verbindung), kann ein Handler mit Wirkung gelaufen sein.
|
|
261
|
+
const unlesbar = UNKNOWN_OUTCOME_CALLS.has(functionName) ? 'unknown' : 'rejected';
|
|
250
262
|
return auswerten(koerper, fehlerName, antwort.status, inhaltstyp, geheimnisse, unlesbar);
|
|
251
263
|
}
|
|
252
264
|
finally {
|
|
@@ -45,6 +45,10 @@ export interface HobexTransactionIdOptions {
|
|
|
45
45
|
*
|
|
46
46
|
* **Der Endpunkt heisst `hobexPayApi`** — mit Suffix; ohne ihn gibt es ihn
|
|
47
47
|
* nicht.
|
|
48
|
+
*
|
|
49
|
+
* **Nie wiederholen.** Bei `isOutcomeUnknown(e)` (Netz, Zeitlimit, HTTP 5xx,
|
|
50
|
+
* unlesbare Antwort) kann die Karte belastet sein: den Stand ueber die
|
|
51
|
+
* Transaktionskennung nachlesen, nicht ein zweites Mal belasten.
|
|
48
52
|
*/
|
|
49
53
|
export declare function hobexPay(transport: InternerTransport, options: HobexPayOptions): Promise<HobexReceipt>;
|
|
50
54
|
/**
|
|
@@ -57,7 +61,11 @@ export declare function hobexPay(transport: InternerTransport, options: HobexPay
|
|
|
57
61
|
* schon im Transport, sobald der Status nicht ausdruecklich Erfolg ist. Ein
|
|
58
62
|
* Wahrheitswert waere hier also immer `true` — eine Luege ueber den
|
|
59
63
|
* Informationsgehalt, an der ein Aufrufer ein `if` aufhaengt, das nie greift.
|
|
60
|
-
* Misserfolg kommt als geworfener Fehler
|
|
64
|
+
* Misserfolg kommt als geworfener Fehler: eine Fehlerhuelle als
|
|
65
|
+
* [KasseneckApiError] mit Code, Netz, Zeitlimit, HTTP 5xx und eine unlesbare
|
|
66
|
+
* Erfolgsantwort mit `outcome: 'unknown'` (die Erstattung kann gelaufen sein).
|
|
67
|
+
* Ein `false` an dieser Stelle luede zum zweiten Versuch ein, also zur
|
|
68
|
+
* doppelten Erstattung. **Nie wiederholen**, sondern den Stand nachlesen.
|
|
61
69
|
*
|
|
62
70
|
* **Der Endpunkt heisst `hobexRefundApi`** — mit Suffix.
|
|
63
71
|
*/
|
|
@@ -41,6 +41,10 @@ const ENDPUNKT_REFUND = 'hobexRefundApi';
|
|
|
41
41
|
*
|
|
42
42
|
* **Der Endpunkt heisst `hobexPayApi`** — mit Suffix; ohne ihn gibt es ihn
|
|
43
43
|
* nicht.
|
|
44
|
+
*
|
|
45
|
+
* **Nie wiederholen.** Bei `isOutcomeUnknown(e)` (Netz, Zeitlimit, HTTP 5xx,
|
|
46
|
+
* unlesbare Antwort) kann die Karte belastet sein: den Stand ueber die
|
|
47
|
+
* Transaktionskennung nachlesen, nicht ein zweites Mal belasten.
|
|
44
48
|
*/
|
|
45
49
|
async function hobexPay(transport, options) {
|
|
46
50
|
const params = zahlungsNutzlast(ENDPUNKT_PAY, options);
|
|
@@ -48,7 +52,11 @@ async function hobexPay(transport, options) {
|
|
|
48
52
|
// geht sie als null raus, nicht gar nicht. Der Transport wirft nur
|
|
49
53
|
// `undefined` weg, `null` bleibt erhalten — genau diese Unterscheidung.
|
|
50
54
|
params['reference'] = options.reference ?? null;
|
|
51
|
-
|
|
55
|
+
const daten = await transport(ENDPUNKT_PAY, params);
|
|
56
|
+
// Erfolg gemeldet heisst: die Karte ist belastet. Ist der Beleg dann
|
|
57
|
+
// unlesbar, bleibt der Ausgang unklar (`response_unreadable`), damit keine
|
|
58
|
+
// App ein zweites Mal belastet.
|
|
59
|
+
return (0, errors_js_1.signiertGelesen)(ENDPUNKT_PAY, () => belegAusNutzlast(daten));
|
|
52
60
|
}
|
|
53
61
|
/**
|
|
54
62
|
* Erstattet eine zuvor ueber die Hobex-Cloud getaetigte Zahlung. Das Backend
|
|
@@ -60,7 +68,11 @@ async function hobexPay(transport, options) {
|
|
|
60
68
|
* schon im Transport, sobald der Status nicht ausdruecklich Erfolg ist. Ein
|
|
61
69
|
* Wahrheitswert waere hier also immer `true` — eine Luege ueber den
|
|
62
70
|
* Informationsgehalt, an der ein Aufrufer ein `if` aufhaengt, das nie greift.
|
|
63
|
-
* Misserfolg kommt als geworfener Fehler
|
|
71
|
+
* Misserfolg kommt als geworfener Fehler: eine Fehlerhuelle als
|
|
72
|
+
* [KasseneckApiError] mit Code, Netz, Zeitlimit, HTTP 5xx und eine unlesbare
|
|
73
|
+
* Erfolgsantwort mit `outcome: 'unknown'` (die Erstattung kann gelaufen sein).
|
|
74
|
+
* Ein `false` an dieser Stelle luede zum zweiten Versuch ein, also zur
|
|
75
|
+
* doppelten Erstattung. **Nie wiederholen**, sondern den Stand nachlesen.
|
|
64
76
|
*
|
|
65
77
|
* **Der Endpunkt heisst `hobexRefundApi`** — mit Suffix.
|
|
66
78
|
*/
|
|
@@ -57,5 +57,8 @@ export declare function createStripeLink(transport: InternerTransport, options:
|
|
|
57
57
|
* **Der Parameter heisst `stripe_sessions_id`** — mit "sessions" im Plural, so
|
|
58
58
|
* das Vorbild und so das Backend. Der naheliegende Singular waere ein fehlender
|
|
59
59
|
* Pflichtparameter.
|
|
60
|
+
*
|
|
61
|
+
* **Nie wiederholen.** Bei `isOutcomeUnknown(e)` (Netz, Zeitlimit, HTTP 5xx,
|
|
62
|
+
* unlesbare Antwort) kann der Einzug gelaufen sein: den Stand nachlesen.
|
|
60
63
|
*/
|
|
61
64
|
export declare function stripeCaptureIntent(transport: InternerTransport, stripeSessionId: string): Promise<StripeCaptureResult>;
|
|
@@ -72,13 +72,18 @@ async function createStripeLink(transport, options) {
|
|
|
72
72
|
* **Der Parameter heisst `stripe_sessions_id`** — mit "sessions" im Plural, so
|
|
73
73
|
* das Vorbild und so das Backend. Der naheliegende Singular waere ein fehlender
|
|
74
74
|
* Pflichtparameter.
|
|
75
|
+
*
|
|
76
|
+
* **Nie wiederholen.** Bei `isOutcomeUnknown(e)` (Netz, Zeitlimit, HTTP 5xx,
|
|
77
|
+
* unlesbare Antwort) kann der Einzug gelaufen sein: den Stand nachlesen.
|
|
75
78
|
*/
|
|
76
79
|
async function stripeCaptureIntent(transport, stripeSessionId) {
|
|
77
80
|
if (typeof stripeSessionId !== 'string' || !stripeSessionId.trim()) {
|
|
78
81
|
throw eingabefehler(ENDPUNKT_CAPTURE, 'stripeSessionId fehlt');
|
|
79
82
|
}
|
|
80
83
|
const daten = await transport(ENDPUNKT_CAPTURE, { stripe_sessions_id: stripeSessionId });
|
|
81
|
-
|
|
84
|
+
// Erfolg gemeldet heisst: eingezogen. Eine unlesbare Nutzlast bleibt
|
|
85
|
+
// Ausgang unklar (`response_unreadable`), nie ein gewoehnlicher Lesefehler.
|
|
86
|
+
return (0, errors_js_1.signiertGelesen)(ENDPUNKT_CAPTURE, () => einzugAusNutzlast(daten));
|
|
82
87
|
}
|
|
83
88
|
/**
|
|
84
89
|
* Positionen in ihre Nutzlast wandeln und dabei die strenge Schreibpfad-
|
package/dist/cjs/version.d.ts
CHANGED
package/dist/cjs/version.js
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* Single-Page-App hat geantwortet, der Aufruf kam nie an),
|
|
21
21
|
* `dialect_mismatch` (Antwort ohne Kennzeichen `Kasseneck-Api-Version: v3`:
|
|
22
22
|
* ein Rand ohne `/v3` hat geantwortet, **Ausgang unklar**) und
|
|
23
|
-
* `response_unreadable` (ein
|
|
23
|
+
* `response_unreadable` (ein Aufruf mit Wirkung meldete Erfolg, die
|
|
24
24
|
* Antwort traegt aber nicht, was er zusagt: **Ausgang unklar**).
|
|
25
25
|
* **Entscheidend ist `outcome`:** `'rejected'` heisst abgelehnt, nichts
|
|
26
26
|
* geschehen, Wiederholen hilft nicht. `'unknown'` heisst: der Vorgang kann
|
|
@@ -29,14 +29,16 @@
|
|
|
29
29
|
* - `KasseneckHttpError` — die Antwort war **keine** verwertbare Huelle:
|
|
30
30
|
* HTTP 500/404 ohne Huelle, leerer Rumpf oder Text statt JSON. Beim
|
|
31
31
|
* Bericht-Download gelten dieselben Gruende fuer alles, was kein PDF ist.
|
|
32
|
-
* `reason` trennt die Faelle maschinenlesbar. Auf einem
|
|
33
|
-
*
|
|
34
|
-
* Kennzeichen, aber leerem oder
|
|
32
|
+
* `reason` trennt die Faelle maschinenlesbar. Auf einem Aufruf mit
|
|
33
|
+
* Wirkung (signierend oder geldbewegend) hat HTTP 5xx
|
|
34
|
+
* `outcome: 'unknown'`, ebenso HTTP 200 mit Kennzeichen, aber leerem oder
|
|
35
|
+
* unlesbarem Rumpf.
|
|
35
36
|
* - `KasseneckNetworkError` — die Antwort kam gar nicht: Netz weg, DNS,
|
|
36
37
|
* abgebrochene Verbindung oder Zeitueberschreitung (`timedOut`). Auch hier
|
|
37
38
|
* gilt `outcome`: war die Anfrage schon unterwegs und ist der Aufruf einer
|
|
38
|
-
*
|
|
39
|
-
*
|
|
39
|
+
* mit Wirkung (signierend: `createReceipt`, `cancelReceipt`,
|
|
40
|
+
* `financeWebService`; geldbewegend: `hobexPayApi`, `hobexRefundApi`,
|
|
41
|
+
* `stripeCaptureIntent`), ist er `'unknown'`.
|
|
40
42
|
* - `KasseneckAuthError` — es kam nicht einmal zur Anfrage, weil die Anmeldung
|
|
41
43
|
* scheiterte (fehlende Zugangsdaten, oder der Token-/Sitzungsgeber warf).
|
|
42
44
|
* In der Browser-Kasse mit ihrer 90-Sekunden-Sitzung ist das Alltag, kein
|
|
@@ -111,7 +113,7 @@ export type ErrorOutcome = 'unknown' | 'rejected';
|
|
|
111
113
|
/**
|
|
112
114
|
* Codes, die das Paket selbst vergibt, nicht der Server: `route_missing`
|
|
113
115
|
* (HTML statt Backend, der Aufruf kam nie an) und `response_unreadable`
|
|
114
|
-
* (ein
|
|
116
|
+
* (ein Aufruf mit Wirkung meldete Erfolg, die Antwort ist aber unlesbar;
|
|
115
117
|
* Ausgang unklar). `dialect_mismatch` vergibt das Paket ebenfalls, der Code
|
|
116
118
|
* gehoert aber schon zum Rand des Servers (`errorCodes.edge`).
|
|
117
119
|
*/
|
|
@@ -142,7 +144,7 @@ export declare class KasseneckApiError extends Error {
|
|
|
142
144
|
/**
|
|
143
145
|
* `'unknown'` bei `dialect_mismatch`, `receipt_outcome_unknown`,
|
|
144
146
|
* `cancellation_outcome_unknown`, `response_unreadable` (Erfolg gemeldet,
|
|
145
|
-
* Antwort eines
|
|
147
|
+
* Antwort eines Aufrufs mit Wirkung aber unlesbar) und
|
|
146
148
|
* `response_translation_failed` (ausser mit `details.handled === false`);
|
|
147
149
|
* sonst `'rejected'`. Bei `'unknown'` nie wiederholen, sondern nachlesen.
|
|
148
150
|
*/
|
|
@@ -162,8 +164,9 @@ export declare class KasseneckHttpError extends Error {
|
|
|
162
164
|
/** Maschinenlesbarer Grund — trennt den Rewrite-Fall vom 500er ohne Textparsen. */
|
|
163
165
|
readonly reason: HttpFailureReason;
|
|
164
166
|
/**
|
|
165
|
-
* `'unknown'` auf einem
|
|
166
|
-
* `cancelReceipt`, `financeWebService`
|
|
167
|
+
* `'unknown'` auf einem Aufruf mit Wirkung (`createReceipt`,
|
|
168
|
+
* `cancelReceipt`, `financeWebService`, `hobexPayApi`, `hobexRefundApi`,
|
|
169
|
+
* `stripeCaptureIntent`) bei HTTP 5xx und bei HTTP 200 mit
|
|
167
170
|
* Kennzeichen, aber unlesbarem Rumpf (`empty-body`, `not-json` auch bei
|
|
168
171
|
* `text/html`, `missing-status`): der Handler kann gelaufen sein, nie wiederholen,
|
|
169
172
|
* sondern nachlesen. Sonst `'rejected'` (auch 4xx).
|
|
@@ -249,3 +252,16 @@ export declare function isKasseneckHttpError(error: unknown): error is Kasseneck
|
|
|
249
252
|
export declare function isKasseneckNetworkError(error: unknown): error is KasseneckNetworkError;
|
|
250
253
|
export declare function isKasseneckAuthError(error: unknown): error is KasseneckAuthError;
|
|
251
254
|
export declare function isKasseneckValidationError(error: unknown): error is KasseneckValidationError;
|
|
255
|
+
/**
|
|
256
|
+
* Liest die Erfolgsantwort eines Aufrufs **mit Wirkung** (signierend oder
|
|
257
|
+
* geldbewegend). Scheitert das Lesen (fehlender Beleg, fehlender Bezug,
|
|
258
|
+
* unbrauchbares Feld oder ein Laufzeitfehler beim Umwandeln), hat der Server
|
|
259
|
+
* trotzdem Erfolg gemeldet: der Beleg ist signiert und im DEP, die Karte
|
|
260
|
+
* belastet bzw. der Einzug gelaufen. Das darf nie als gewoehnlicher Fehler
|
|
261
|
+
* enden, sonst kassiert die Kasse ein zweites Mal. Darum wird daraus
|
|
262
|
+
* `KasseneckApiError` mit Code `response_unreadable` und `outcome: 'unknown'`.
|
|
263
|
+
* Der Grund stammt vom Paket; aus der Antwort selbst wird nichts uebernommen.
|
|
264
|
+
*
|
|
265
|
+
* Paketintern (receipts.ts, payments/); nicht Teil der Paketoberflaeche.
|
|
266
|
+
*/
|
|
267
|
+
export declare function signiertGelesen<T>(functionName: string, lesen: () => T): T;
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* Single-Page-App hat geantwortet, der Aufruf kam nie an),
|
|
21
21
|
* `dialect_mismatch` (Antwort ohne Kennzeichen `Kasseneck-Api-Version: v3`:
|
|
22
22
|
* ein Rand ohne `/v3` hat geantwortet, **Ausgang unklar**) und
|
|
23
|
-
* `response_unreadable` (ein
|
|
23
|
+
* `response_unreadable` (ein Aufruf mit Wirkung meldete Erfolg, die
|
|
24
24
|
* Antwort traegt aber nicht, was er zusagt: **Ausgang unklar**).
|
|
25
25
|
* **Entscheidend ist `outcome`:** `'rejected'` heisst abgelehnt, nichts
|
|
26
26
|
* geschehen, Wiederholen hilft nicht. `'unknown'` heisst: der Vorgang kann
|
|
@@ -29,14 +29,16 @@
|
|
|
29
29
|
* - `KasseneckHttpError` — die Antwort war **keine** verwertbare Huelle:
|
|
30
30
|
* HTTP 500/404 ohne Huelle, leerer Rumpf oder Text statt JSON. Beim
|
|
31
31
|
* Bericht-Download gelten dieselben Gruende fuer alles, was kein PDF ist.
|
|
32
|
-
* `reason` trennt die Faelle maschinenlesbar. Auf einem
|
|
33
|
-
*
|
|
34
|
-
* Kennzeichen, aber leerem oder
|
|
32
|
+
* `reason` trennt die Faelle maschinenlesbar. Auf einem Aufruf mit
|
|
33
|
+
* Wirkung (signierend oder geldbewegend) hat HTTP 5xx
|
|
34
|
+
* `outcome: 'unknown'`, ebenso HTTP 200 mit Kennzeichen, aber leerem oder
|
|
35
|
+
* unlesbarem Rumpf.
|
|
35
36
|
* - `KasseneckNetworkError` — die Antwort kam gar nicht: Netz weg, DNS,
|
|
36
37
|
* abgebrochene Verbindung oder Zeitueberschreitung (`timedOut`). Auch hier
|
|
37
38
|
* gilt `outcome`: war die Anfrage schon unterwegs und ist der Aufruf einer
|
|
38
|
-
*
|
|
39
|
-
*
|
|
39
|
+
* mit Wirkung (signierend: `createReceipt`, `cancelReceipt`,
|
|
40
|
+
* `financeWebService`; geldbewegend: `hobexPayApi`, `hobexRefundApi`,
|
|
41
|
+
* `stripeCaptureIntent`), ist er `'unknown'`.
|
|
40
42
|
* - `KasseneckAuthError` — es kam nicht einmal zur Anfrage, weil die Anmeldung
|
|
41
43
|
* scheiterte (fehlende Zugangsdaten, oder der Token-/Sitzungsgeber warf).
|
|
42
44
|
* In der Browser-Kasse mit ihrer 90-Sekunden-Sitzung ist das Alltag, kein
|
|
@@ -225,7 +227,7 @@ function ausgangAusCode(code, details) {
|
|
|
225
227
|
/**
|
|
226
228
|
* Codes, die das Paket selbst vergibt, nicht der Server: `route_missing`
|
|
227
229
|
* (HTML statt Backend, der Aufruf kam nie an) und `response_unreadable`
|
|
228
|
-
* (ein
|
|
230
|
+
* (ein Aufruf mit Wirkung meldete Erfolg, die Antwort ist aber unlesbar;
|
|
229
231
|
* Ausgang unklar). `dialect_mismatch` vergibt das Paket ebenfalls, der Code
|
|
230
232
|
* gehoert aber schon zum Rand des Servers (`errorCodes.edge`).
|
|
231
233
|
*/
|
|
@@ -255,7 +257,7 @@ export class KasseneckApiError extends Error {
|
|
|
255
257
|
/**
|
|
256
258
|
* `'unknown'` bei `dialect_mismatch`, `receipt_outcome_unknown`,
|
|
257
259
|
* `cancellation_outcome_unknown`, `response_unreadable` (Erfolg gemeldet,
|
|
258
|
-
* Antwort eines
|
|
260
|
+
* Antwort eines Aufrufs mit Wirkung aber unlesbar) und
|
|
259
261
|
* `response_translation_failed` (ausser mit `details.handled === false`);
|
|
260
262
|
* sonst `'rejected'`. Bei `'unknown'` nie wiederholen, sondern nachlesen.
|
|
261
263
|
*/
|
|
@@ -289,8 +291,9 @@ export class KasseneckHttpError extends Error {
|
|
|
289
291
|
/** Maschinenlesbarer Grund — trennt den Rewrite-Fall vom 500er ohne Textparsen. */
|
|
290
292
|
reason;
|
|
291
293
|
/**
|
|
292
|
-
* `'unknown'` auf einem
|
|
293
|
-
* `cancelReceipt`, `financeWebService`
|
|
294
|
+
* `'unknown'` auf einem Aufruf mit Wirkung (`createReceipt`,
|
|
295
|
+
* `cancelReceipt`, `financeWebService`, `hobexPayApi`, `hobexRefundApi`,
|
|
296
|
+
* `stripeCaptureIntent`) bei HTTP 5xx und bei HTTP 200 mit
|
|
294
297
|
* Kennzeichen, aber unlesbarem Rumpf (`empty-body`, `not-json` auch bei
|
|
295
298
|
* `text/html`, `missing-status`): der Handler kann gelaufen sein, nie wiederholen,
|
|
296
299
|
* sondern nachlesen. Sonst `'rejected'` (auch 4xx).
|
|
@@ -410,3 +413,24 @@ export function isKasseneckAuthError(error) {
|
|
|
410
413
|
export function isKasseneckValidationError(error) {
|
|
411
414
|
return error instanceof KasseneckValidationError;
|
|
412
415
|
}
|
|
416
|
+
/**
|
|
417
|
+
* Liest die Erfolgsantwort eines Aufrufs **mit Wirkung** (signierend oder
|
|
418
|
+
* geldbewegend). Scheitert das Lesen (fehlender Beleg, fehlender Bezug,
|
|
419
|
+
* unbrauchbares Feld oder ein Laufzeitfehler beim Umwandeln), hat der Server
|
|
420
|
+
* trotzdem Erfolg gemeldet: der Beleg ist signiert und im DEP, die Karte
|
|
421
|
+
* belastet bzw. der Einzug gelaufen. Das darf nie als gewoehnlicher Fehler
|
|
422
|
+
* enden, sonst kassiert die Kasse ein zweites Mal. Darum wird daraus
|
|
423
|
+
* `KasseneckApiError` mit Code `response_unreadable` und `outcome: 'unknown'`.
|
|
424
|
+
* Der Grund stammt vom Paket; aus der Antwort selbst wird nichts uebernommen.
|
|
425
|
+
*
|
|
426
|
+
* Paketintern (receipts.ts, payments/); nicht Teil der Paketoberflaeche.
|
|
427
|
+
*/
|
|
428
|
+
export function signiertGelesen(functionName, lesen) {
|
|
429
|
+
try {
|
|
430
|
+
return lesen();
|
|
431
|
+
}
|
|
432
|
+
catch (ursache) {
|
|
433
|
+
const grund = ursache instanceof KasseneckValidationError ? ursache.reason : 'Antwort nicht lesbar';
|
|
434
|
+
throw new KasseneckApiError(functionName, `Erfolg gemeldet, Antwort aber unlesbar (${grund}). Der Vorgang kann ausgefuehrt sein: nicht wiederholen, sondern nachlesen.`, {}, 'response_unreadable');
|
|
435
|
+
}
|
|
436
|
+
}
|
|
@@ -2,7 +2,7 @@ import { ReceiptType, KeckPaymentMethod, CreditCardProvider, VoucherAction, Vouc
|
|
|
2
2
|
import { fromReceiptCompanyPayload, fromReceiptPayload, toReceiptItemPayload, toVoucherPayload, receiptItemIsValid, voucherIsValid, fromReceiptSummaryPayload, isCancellationReason, RECEIPT_EMAIL_VIAS, readRegistrationInfo, } from '../models/index.js';
|
|
3
3
|
import { parseServerTimeStamp, toViennaWallClock } from '../vienna-time.js';
|
|
4
4
|
import { euroToCents } from '../money.js';
|
|
5
|
-
import {
|
|
5
|
+
import { KasseneckValidationError, isKasseneckApiError, signiertGelesen } from './errors.js';
|
|
6
6
|
import { buildReceiptLayout } from '../receipt/layout.js';
|
|
7
7
|
/**
|
|
8
8
|
* Das Zeilenmodell zum Drucken und Anzeigen eines Belegs aus einer Antwort.
|
|
@@ -666,24 +666,6 @@ function kartenanbieter(wert) {
|
|
|
666
666
|
function eingabefehler(grund) {
|
|
667
667
|
return new KasseneckValidationError('createReceipt', grund, 'request');
|
|
668
668
|
}
|
|
669
|
-
/**
|
|
670
|
-
* Liest die Erfolgsantwort eines **signierenden** Aufrufs. Scheitert das
|
|
671
|
-
* Lesen (fehlender Beleg, fehlender Bezug, unbrauchbares Feld oder ein
|
|
672
|
-
* Laufzeitfehler beim Umwandeln), hat der Server trotzdem Erfolg gemeldet:
|
|
673
|
-
* der Beleg ist signiert und im DEP. Das darf nie als gewoehnlicher Fehler
|
|
674
|
-
* enden, sonst kassiert die Kasse ein zweites Mal. Darum wird daraus
|
|
675
|
-
* `KasseneckApiError` mit Code `response_unreadable` und `outcome: 'unknown'`.
|
|
676
|
-
* Der Grund stammt vom Paket; aus der Antwort selbst wird nichts uebernommen.
|
|
677
|
-
*/
|
|
678
|
-
function signiertGelesen(functionName, lesen) {
|
|
679
|
-
try {
|
|
680
|
-
return lesen();
|
|
681
|
-
}
|
|
682
|
-
catch (ursache) {
|
|
683
|
-
const grund = ursache instanceof KasseneckValidationError ? ursache.reason : 'Antwort nicht lesbar';
|
|
684
|
-
throw new KasseneckApiError(functionName, `Erfolg gemeldet, Antwort aber unlesbar (${grund}). Der Vorgang kann ausgefuehrt sein: nicht wiederholen, sondern nachlesen.`, {}, 'response_unreadable');
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
669
|
/** Die Antwort meldete Erfolg, trug aber nicht, was der Aufruf zusagt. */
|
|
688
670
|
function antwortfehler(functionName, grund) {
|
|
689
671
|
return new KasseneckValidationError(functionName, grund, 'response');
|
|
@@ -57,10 +57,22 @@ const KASSENECK_HOSTS = new Set(['api.kasseneck.at', 'kasse.kasseneck.at']);
|
|
|
57
57
|
/** Die 1.x-Linie spricht nur `/v3`: jede Basis endet so (`/v3` oder `/api/v3`). */
|
|
58
58
|
const V3_ENDE = /\/v3$/;
|
|
59
59
|
/**
|
|
60
|
-
* Aufrufe, die
|
|
61
|
-
*
|
|
60
|
+
* Aufrufe mit Wirkung, die nie blind wiederholt werden duerfen: sie signieren
|
|
61
|
+
* (`createReceipt`, `cancelReceipt`), loesen bei FinanzOnline etwas aus
|
|
62
|
+
* (`financeWebService`) oder bewegen Geld (`hobexPayApi` belastet eine Karte,
|
|
63
|
+
* `hobexRefundApi` erstattet, `stripeCaptureIntent` zieht eine vorgemerkte
|
|
64
|
+
* Zahlung ein). Scheitert einer, nachdem die Anfrage unterwegs war (Netz,
|
|
65
|
+
* Zeitlimit, HTTP 5xx, unlesbare Erfolgsantwort, HTML mit Kennzeichen), ist
|
|
66
|
+
* sein Ausgang offen.
|
|
62
67
|
*/
|
|
63
|
-
const
|
|
68
|
+
const UNKNOWN_OUTCOME_CALLS = new Set([
|
|
69
|
+
'createReceipt',
|
|
70
|
+
'cancelReceipt',
|
|
71
|
+
'financeWebService',
|
|
72
|
+
'hobexPayApi',
|
|
73
|
+
'hobexRefundApi',
|
|
74
|
+
'stripeCaptureIntent',
|
|
75
|
+
]);
|
|
64
76
|
/**
|
|
65
77
|
* Produkte, die das Backend in `Kasseneck-Client` zaehlt (Positivliste,
|
|
66
78
|
* Nachtrag §6); alles andere zaehlte dort als `ungueltig`. Das Paket weist
|
|
@@ -157,9 +169,9 @@ function createCore(options) {
|
|
|
157
169
|
return ursache;
|
|
158
170
|
}
|
|
159
171
|
// Bis hierher kam keine verwertbare Antwort: Netz weg oder Zeitlimit.
|
|
160
|
-
// Die Anfrage war schon unterwegs: bei einem
|
|
161
|
-
// der Beleg entstanden sein (Ausgang unklar, nachlesen).
|
|
162
|
-
return new KasseneckNetworkError(fehlerName, abbruch.signal.aborted, zeitlimitMs, causeDigest(ursache, geheimnisse),
|
|
172
|
+
// Die Anfrage war schon unterwegs: bei einem Aufruf mit Wirkung kann
|
|
173
|
+
// der Beleg bzw. die Zahlung entstanden sein (Ausgang unklar, nachlesen).
|
|
174
|
+
return new KasseneckNetworkError(fehlerName, abbruch.signal.aborted, zeitlimitMs, causeDigest(ursache, geheimnisse), UNKNOWN_OUTCOME_CALLS.has(functionName) ? 'unknown' : 'rejected');
|
|
163
175
|
};
|
|
164
176
|
const basis = basisFuer(functionName);
|
|
165
177
|
const url = `${basis}/${encodeURIComponent(functionName)}`;
|
|
@@ -212,18 +224,18 @@ function createCore(options) {
|
|
|
212
224
|
if (fehler)
|
|
213
225
|
throw fehler;
|
|
214
226
|
}
|
|
215
|
-
// 5xx auf einem
|
|
216
|
-
const ausgang = antwort.status >= 500 &&
|
|
227
|
+
// 5xx auf einem Aufruf mit Wirkung: der Handler kann gelaufen sein.
|
|
228
|
+
const ausgang = antwort.status >= 500 && UNKNOWN_OUTCOME_CALLS.has(functionName) ? 'unknown' : 'rejected';
|
|
217
229
|
throw new KasseneckHttpError(fehlerName, antwort.status, inhaltstyp, 'server-error', ausgang);
|
|
218
230
|
}
|
|
219
231
|
// HTTP 200 mit HTML: die Auffangregel der Single-Page-App hat den Aufruf
|
|
220
232
|
// bedient, keine Function hat ihn gesehen (Nachtrag §5.4, R15).
|
|
221
233
|
if (inhaltstyp !== undefined && /^\s*text\/html\b/i.test(inhaltstyp)) {
|
|
222
234
|
// Mit Kennzeichen hat der `/v3`-Rand den Aufruf gesehen (etwa ein
|
|
223
|
-
// Proxy, der nur den Inhaltstyp umschreibt). Bei einem
|
|
224
|
-
//
|
|
235
|
+
// Proxy, der nur den Inhaltstyp umschreibt). Bei einem Aufruf mit
|
|
236
|
+
// Wirkung kann der Beleg bzw. die Zahlung dann entstanden sein: unlesbarer Rumpf,
|
|
225
237
|
// Ausgang unklar, statt `route_missing`.
|
|
226
|
-
if (
|
|
238
|
+
if (UNKNOWN_OUTCOME_CALLS.has(functionName) && traegtKennzeichen(antwort)) {
|
|
227
239
|
throw new KasseneckHttpError(fehlerName, antwort.status, inhaltstyp, 'not-json', 'unknown');
|
|
228
240
|
}
|
|
229
241
|
throw new KasseneckApiError(fehlerName, 'Route fehlt: die Antwort ist eine HTML-Seite statt des Backends', {}, 'route_missing');
|
|
@@ -240,8 +252,8 @@ function createCore(options) {
|
|
|
240
252
|
}
|
|
241
253
|
// Ab hier kam HTTP 200 mit Kennzeichen: der `/v3`-Rand hat den Aufruf
|
|
242
254
|
// gesehen. Ist der Rumpf dann unlesbar (gekuerzt von einem Proxy,
|
|
243
|
-
// abgebrochene Verbindung), kann ein
|
|
244
|
-
const unlesbar =
|
|
255
|
+
// abgebrochene Verbindung), kann ein Handler mit Wirkung gelaufen sein.
|
|
256
|
+
const unlesbar = UNKNOWN_OUTCOME_CALLS.has(functionName) ? 'unknown' : 'rejected';
|
|
245
257
|
return auswerten(koerper, fehlerName, antwort.status, inhaltstyp, geheimnisse, unlesbar);
|
|
246
258
|
}
|
|
247
259
|
finally {
|
|
@@ -45,6 +45,10 @@ export interface HobexTransactionIdOptions {
|
|
|
45
45
|
*
|
|
46
46
|
* **Der Endpunkt heisst `hobexPayApi`** — mit Suffix; ohne ihn gibt es ihn
|
|
47
47
|
* nicht.
|
|
48
|
+
*
|
|
49
|
+
* **Nie wiederholen.** Bei `isOutcomeUnknown(e)` (Netz, Zeitlimit, HTTP 5xx,
|
|
50
|
+
* unlesbare Antwort) kann die Karte belastet sein: den Stand ueber die
|
|
51
|
+
* Transaktionskennung nachlesen, nicht ein zweites Mal belasten.
|
|
48
52
|
*/
|
|
49
53
|
export declare function hobexPay(transport: InternerTransport, options: HobexPayOptions): Promise<HobexReceipt>;
|
|
50
54
|
/**
|
|
@@ -57,7 +61,11 @@ export declare function hobexPay(transport: InternerTransport, options: HobexPay
|
|
|
57
61
|
* schon im Transport, sobald der Status nicht ausdruecklich Erfolg ist. Ein
|
|
58
62
|
* Wahrheitswert waere hier also immer `true` — eine Luege ueber den
|
|
59
63
|
* Informationsgehalt, an der ein Aufrufer ein `if` aufhaengt, das nie greift.
|
|
60
|
-
* Misserfolg kommt als geworfener Fehler
|
|
64
|
+
* Misserfolg kommt als geworfener Fehler: eine Fehlerhuelle als
|
|
65
|
+
* [KasseneckApiError] mit Code, Netz, Zeitlimit, HTTP 5xx und eine unlesbare
|
|
66
|
+
* Erfolgsantwort mit `outcome: 'unknown'` (die Erstattung kann gelaufen sein).
|
|
67
|
+
* Ein `false` an dieser Stelle luede zum zweiten Versuch ein, also zur
|
|
68
|
+
* doppelten Erstattung. **Nie wiederholen**, sondern den Stand nachlesen.
|
|
61
69
|
*
|
|
62
70
|
* **Der Endpunkt heisst `hobexRefundApi`** — mit Suffix.
|
|
63
71
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { fromHobexReceiptPayload } from '../models/index.js';
|
|
2
|
-
import { KasseneckValidationError } from '../client/errors.js';
|
|
2
|
+
import { KasseneckValidationError, signiertGelesen } from '../client/errors.js';
|
|
3
3
|
import { toViennaWallClock } from '../vienna-time.js';
|
|
4
4
|
import { centsToEuro } from '../money.js';
|
|
5
5
|
/**
|
|
@@ -36,6 +36,10 @@ const ENDPUNKT_REFUND = 'hobexRefundApi';
|
|
|
36
36
|
*
|
|
37
37
|
* **Der Endpunkt heisst `hobexPayApi`** — mit Suffix; ohne ihn gibt es ihn
|
|
38
38
|
* nicht.
|
|
39
|
+
*
|
|
40
|
+
* **Nie wiederholen.** Bei `isOutcomeUnknown(e)` (Netz, Zeitlimit, HTTP 5xx,
|
|
41
|
+
* unlesbare Antwort) kann die Karte belastet sein: den Stand ueber die
|
|
42
|
+
* Transaktionskennung nachlesen, nicht ein zweites Mal belasten.
|
|
39
43
|
*/
|
|
40
44
|
export async function hobexPay(transport, options) {
|
|
41
45
|
const params = zahlungsNutzlast(ENDPUNKT_PAY, options);
|
|
@@ -43,7 +47,11 @@ export async function hobexPay(transport, options) {
|
|
|
43
47
|
// geht sie als null raus, nicht gar nicht. Der Transport wirft nur
|
|
44
48
|
// `undefined` weg, `null` bleibt erhalten — genau diese Unterscheidung.
|
|
45
49
|
params['reference'] = options.reference ?? null;
|
|
46
|
-
|
|
50
|
+
const daten = await transport(ENDPUNKT_PAY, params);
|
|
51
|
+
// Erfolg gemeldet heisst: die Karte ist belastet. Ist der Beleg dann
|
|
52
|
+
// unlesbar, bleibt der Ausgang unklar (`response_unreadable`), damit keine
|
|
53
|
+
// App ein zweites Mal belastet.
|
|
54
|
+
return signiertGelesen(ENDPUNKT_PAY, () => belegAusNutzlast(daten));
|
|
47
55
|
}
|
|
48
56
|
/**
|
|
49
57
|
* Erstattet eine zuvor ueber die Hobex-Cloud getaetigte Zahlung. Das Backend
|
|
@@ -55,7 +63,11 @@ export async function hobexPay(transport, options) {
|
|
|
55
63
|
* schon im Transport, sobald der Status nicht ausdruecklich Erfolg ist. Ein
|
|
56
64
|
* Wahrheitswert waere hier also immer `true` — eine Luege ueber den
|
|
57
65
|
* Informationsgehalt, an der ein Aufrufer ein `if` aufhaengt, das nie greift.
|
|
58
|
-
* Misserfolg kommt als geworfener Fehler
|
|
66
|
+
* Misserfolg kommt als geworfener Fehler: eine Fehlerhuelle als
|
|
67
|
+
* [KasseneckApiError] mit Code, Netz, Zeitlimit, HTTP 5xx und eine unlesbare
|
|
68
|
+
* Erfolgsantwort mit `outcome: 'unknown'` (die Erstattung kann gelaufen sein).
|
|
69
|
+
* Ein `false` an dieser Stelle luede zum zweiten Versuch ein, also zur
|
|
70
|
+
* doppelten Erstattung. **Nie wiederholen**, sondern den Stand nachlesen.
|
|
59
71
|
*
|
|
60
72
|
* **Der Endpunkt heisst `hobexRefundApi`** — mit Suffix.
|
|
61
73
|
*/
|
|
@@ -57,5 +57,8 @@ export declare function createStripeLink(transport: InternerTransport, options:
|
|
|
57
57
|
* **Der Parameter heisst `stripe_sessions_id`** — mit "sessions" im Plural, so
|
|
58
58
|
* das Vorbild und so das Backend. Der naheliegende Singular waere ein fehlender
|
|
59
59
|
* Pflichtparameter.
|
|
60
|
+
*
|
|
61
|
+
* **Nie wiederholen.** Bei `isOutcomeUnknown(e)` (Netz, Zeitlimit, HTTP 5xx,
|
|
62
|
+
* unlesbare Antwort) kann der Einzug gelaufen sein: den Stand nachlesen.
|
|
60
63
|
*/
|
|
61
64
|
export declare function stripeCaptureIntent(transport: InternerTransport, stripeSessionId: string): Promise<StripeCaptureResult>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { StripeLinkMode } from '../enums/index.js';
|
|
2
2
|
import { fromStripeUrlSessionPayload, toReceiptItemPayload, receiptItemIsValid, } from '../models/index.js';
|
|
3
|
-
import { KasseneckValidationError } from '../client/errors.js';
|
|
3
|
+
import { KasseneckValidationError, signiertGelesen } from '../client/errors.js';
|
|
4
4
|
/**
|
|
5
5
|
* Stripe-Zahlungslinks — Zwilling der Stripe-Aufrufe in
|
|
6
6
|
* kasseneck_api/lib/kasseneck_api.dart (Zeilen 443-484).
|
|
@@ -68,13 +68,18 @@ export async function createStripeLink(transport, options) {
|
|
|
68
68
|
* **Der Parameter heisst `stripe_sessions_id`** — mit "sessions" im Plural, so
|
|
69
69
|
* das Vorbild und so das Backend. Der naheliegende Singular waere ein fehlender
|
|
70
70
|
* Pflichtparameter.
|
|
71
|
+
*
|
|
72
|
+
* **Nie wiederholen.** Bei `isOutcomeUnknown(e)` (Netz, Zeitlimit, HTTP 5xx,
|
|
73
|
+
* unlesbare Antwort) kann der Einzug gelaufen sein: den Stand nachlesen.
|
|
71
74
|
*/
|
|
72
75
|
export async function stripeCaptureIntent(transport, stripeSessionId) {
|
|
73
76
|
if (typeof stripeSessionId !== 'string' || !stripeSessionId.trim()) {
|
|
74
77
|
throw eingabefehler(ENDPUNKT_CAPTURE, 'stripeSessionId fehlt');
|
|
75
78
|
}
|
|
76
79
|
const daten = await transport(ENDPUNKT_CAPTURE, { stripe_sessions_id: stripeSessionId });
|
|
77
|
-
|
|
80
|
+
// Erfolg gemeldet heisst: eingezogen. Eine unlesbare Nutzlast bleibt
|
|
81
|
+
// Ausgang unklar (`response_unreadable`), nie ein gewoehnlicher Lesefehler.
|
|
82
|
+
return signiertGelesen(ENDPUNKT_CAPTURE, () => einzugAusNutzlast(daten));
|
|
78
83
|
}
|
|
79
84
|
/**
|
|
80
85
|
* Positionen in ihre Nutzlast wandeln und dabei die strenge Schreibpfad-
|
package/dist/esm/version.d.ts
CHANGED
package/dist/esm/version.js
CHANGED
package/fixtures/pos-texts.json
CHANGED
package/fixtures/surface.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kreiseck/kasseneck-api",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.2",
|
|
4
4
|
"description": "Austrian fiscal cash register (RKSV) client for JavaScript and TypeScript: signed receipts, cancellations, card payments, receipt printing (ESC/POS), invoices. RKSV-Registrierkasse für JavaScript und TypeScript.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Kreiseck",
|