@coderoftime/fints 0.1.0 → 0.2.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.
Files changed (272) hide show
  1. package/CHANGELOG.md +142 -1
  2. package/README.md +127 -37
  3. package/dist/client.d.ts +48 -0
  4. package/dist/client.d.ts.map +1 -0
  5. package/dist/client.js +199 -0
  6. package/dist/client.js.map +1 -0
  7. package/dist/core/account.d.ts +35 -7
  8. package/dist/core/account.d.ts.map +1 -1
  9. package/dist/core/account.js +84 -13
  10. package/dist/core/account.js.map +1 -1
  11. package/dist/core/amount.d.ts +2 -1
  12. package/dist/core/amount.d.ts.map +1 -1
  13. package/dist/core/amount.js +4 -1
  14. package/dist/core/amount.js.map +1 -1
  15. package/dist/core/balance.d.ts +1 -0
  16. package/dist/core/balance.d.ts.map +1 -1
  17. package/dist/core/balance.js +6 -5
  18. package/dist/core/balance.js.map +1 -1
  19. package/dist/core/bank-info.d.ts +1 -1
  20. package/dist/core/bank-info.js +1 -1
  21. package/dist/core/client.d.ts +50 -15
  22. package/dist/core/client.d.ts.map +1 -1
  23. package/dist/core/config.d.ts +28 -16
  24. package/dist/core/config.d.ts.map +1 -1
  25. package/dist/core/config.js +66 -26
  26. package/dist/core/config.js.map +1 -1
  27. package/dist/core/errors.d.ts +85 -23
  28. package/dist/core/errors.d.ts.map +1 -1
  29. package/dist/core/errors.js +112 -47
  30. package/dist/core/errors.js.map +1 -1
  31. package/dist/core/feedback.d.ts +4 -1
  32. package/dist/core/feedback.d.ts.map +1 -1
  33. package/dist/core/feedback.js +4 -1
  34. package/dist/core/feedback.js.map +1 -1
  35. package/dist/core/transaction.d.ts +6 -1
  36. package/dist/core/transaction.d.ts.map +1 -1
  37. package/dist/core/transaction.js +9 -4
  38. package/dist/core/transaction.js.map +1 -1
  39. package/dist/core/transport.d.ts +33 -6
  40. package/dist/core/transport.d.ts.map +1 -1
  41. package/dist/core/transport.js +77 -20
  42. package/dist/core/transport.js.map +1 -1
  43. package/dist/formats/camt.d.ts +24 -4
  44. package/dist/formats/camt.d.ts.map +1 -1
  45. package/dist/formats/camt.js +124 -41
  46. package/dist/formats/camt.js.map +1 -1
  47. package/dist/formats/mt940.d.ts +15 -3
  48. package/dist/formats/mt940.d.ts.map +1 -1
  49. package/dist/formats/mt940.js +161 -82
  50. package/dist/formats/mt940.js.map +1 -1
  51. package/dist/formats/readers.d.ts +6 -2
  52. package/dist/formats/readers.d.ts.map +1 -1
  53. package/dist/formats/readers.js +24 -29
  54. package/dist/formats/readers.js.map +1 -1
  55. package/dist/formats/record-budget.d.ts +7 -0
  56. package/dist/formats/record-budget.d.ts.map +1 -0
  57. package/dist/formats/record-budget.js +17 -0
  58. package/dist/formats/record-budget.js.map +1 -0
  59. package/dist/formats/sepa-purpose.d.ts +6 -0
  60. package/dist/formats/sepa-purpose.d.ts.map +1 -1
  61. package/dist/formats/sepa-purpose.js +23 -2
  62. package/dist/formats/sepa-purpose.js.map +1 -1
  63. package/dist/index.d.ts +3 -3
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +2 -2
  66. package/dist/index.js.map +1 -1
  67. package/dist/session/app-approval.d.ts +11 -5
  68. package/dist/session/app-approval.d.ts.map +1 -1
  69. package/dist/session/app-approval.js +23 -12
  70. package/dist/session/app-approval.js.map +1 -1
  71. package/dist/session/dialog-runner.d.ts +7 -10
  72. package/dist/session/dialog-runner.d.ts.map +1 -1
  73. package/dist/session/dialog-runner.js +15 -16
  74. package/dist/session/dialog-runner.js.map +1 -1
  75. package/dist/session/login-guard.d.ts +54 -49
  76. package/dist/session/login-guard.d.ts.map +1 -1
  77. package/dist/session/login-guard.js +156 -84
  78. package/dist/session/login-guard.js.map +1 -1
  79. package/dist/session/sepa-accounts.d.ts +20 -14
  80. package/dist/session/sepa-accounts.d.ts.map +1 -1
  81. package/dist/session/sepa-accounts.js +62 -46
  82. package/dist/session/sepa-accounts.js.map +1 -1
  83. package/dist/session/session-setup.d.ts +33 -25
  84. package/dist/session/session-setup.d.ts.map +1 -1
  85. package/dist/session/session-setup.js +87 -65
  86. package/dist/session/session-setup.js.map +1 -1
  87. package/dist/session/session-state.d.ts +9 -6
  88. package/dist/session/session-state.d.ts.map +1 -1
  89. package/dist/session/session-state.js +52 -17
  90. package/dist/session/session-state.js.map +1 -1
  91. package/dist/session/signed-dialog.d.ts +77 -0
  92. package/dist/session/signed-dialog.d.ts.map +1 -0
  93. package/dist/session/signed-dialog.js +170 -0
  94. package/dist/session/signed-dialog.js.map +1 -0
  95. package/dist/session/tan-selection.d.ts +26 -18
  96. package/dist/session/tan-selection.d.ts.map +1 -1
  97. package/dist/session/tan-selection.js +44 -33
  98. package/dist/session/tan-selection.js.map +1 -1
  99. package/dist/session/user-prompts.d.ts +8 -4
  100. package/dist/session/user-prompts.d.ts.map +1 -1
  101. package/dist/session/user-prompts.js +34 -14
  102. package/dist/session/user-prompts.js.map +1 -1
  103. package/dist/tan/challenge.d.ts +34 -13
  104. package/dist/tan/challenge.d.ts.map +1 -1
  105. package/dist/tan/challenge.js +25 -26
  106. package/dist/tan/challenge.js.map +1 -1
  107. package/dist/tan/flicker.d.ts +7 -1
  108. package/dist/tan/flicker.d.ts.map +1 -1
  109. package/dist/tan/flicker.js +40 -6
  110. package/dist/tan/flicker.js.map +1 -1
  111. package/dist/tan/tan-image.d.ts +3 -2
  112. package/dist/tan/tan-image.d.ts.map +1 -1
  113. package/dist/tan/tan-image.js +7 -5
  114. package/dist/tan/tan-image.js.map +1 -1
  115. package/dist/tan/tan-models.d.ts +5 -1
  116. package/dist/tan/tan-models.d.ts.map +1 -1
  117. package/dist/tan/tan-models.js +10 -5
  118. package/dist/tan/tan-models.js.map +1 -1
  119. package/dist/util/async.d.ts +0 -2
  120. package/dist/util/async.d.ts.map +1 -1
  121. package/dist/util/async.js +0 -4
  122. package/dist/util/async.js.map +1 -1
  123. package/dist/util/bytes.d.ts +2 -3
  124. package/dist/util/bytes.d.ts.map +1 -1
  125. package/dist/util/bytes.js +12 -20
  126. package/dist/util/bytes.js.map +1 -1
  127. package/dist/util/call-context.d.ts.map +1 -1
  128. package/dist/util/call-context.js +3 -2
  129. package/dist/util/call-context.js.map +1 -1
  130. package/dist/util/dates.d.ts +9 -16
  131. package/dist/util/dates.d.ts.map +1 -1
  132. package/dist/util/dates.js +39 -19
  133. package/dist/util/dates.js.map +1 -1
  134. package/dist/util/errors.d.ts +9 -1
  135. package/dist/util/errors.d.ts.map +1 -1
  136. package/dist/util/errors.js +12 -3
  137. package/dist/util/errors.js.map +1 -1
  138. package/dist/util/json.d.ts +4 -0
  139. package/dist/util/json.d.ts.map +1 -1
  140. package/dist/util/json.js +18 -0
  141. package/dist/util/json.js.map +1 -1
  142. package/dist/util/text.d.ts +21 -4
  143. package/dist/util/text.d.ts.map +1 -1
  144. package/dist/util/text.js +42 -13
  145. package/dist/util/text.js.map +1 -1
  146. package/dist/util/xml.d.ts +4 -3
  147. package/dist/util/xml.d.ts.map +1 -1
  148. package/dist/util/xml.js +37 -26
  149. package/dist/util/xml.js.map +1 -1
  150. package/dist/v3/account-extension.d.ts.map +1 -1
  151. package/dist/v3/account-extension.js +7 -10
  152. package/dist/v3/account-extension.js.map +1 -1
  153. package/dist/v3/bank-connection.d.ts +34 -0
  154. package/dist/v3/bank-connection.d.ts.map +1 -0
  155. package/dist/v3/bank-connection.js +58 -0
  156. package/dist/v3/bank-connection.js.map +1 -0
  157. package/dist/v3/dialog.d.ts +80 -46
  158. package/dist/v3/dialog.d.ts.map +1 -1
  159. package/dist/v3/dialog.js +162 -59
  160. package/dist/v3/dialog.js.map +1 -1
  161. package/dist/v3/message.d.ts +17 -4
  162. package/dist/v3/message.d.ts.map +1 -1
  163. package/dist/v3/message.js +62 -18
  164. package/dist/v3/message.js.map +1 -1
  165. package/dist/v3/parameter-store.d.ts +75 -0
  166. package/dist/v3/parameter-store.d.ts.map +1 -0
  167. package/dist/v3/parameter-store.js +143 -0
  168. package/dist/v3/parameter-store.js.map +1 -0
  169. package/dist/v3/parameters.d.ts +38 -12
  170. package/dist/v3/parameters.d.ts.map +1 -1
  171. package/dist/v3/parameters.js +91 -37
  172. package/dist/v3/parameters.js.map +1 -1
  173. package/dist/v3/protocol.d.ts +43 -50
  174. package/dist/v3/protocol.d.ts.map +1 -1
  175. package/dist/v3/protocol.js +58 -208
  176. package/dist/v3/protocol.js.map +1 -1
  177. package/dist/v3/queries.d.ts +51 -0
  178. package/dist/v3/queries.d.ts.map +1 -0
  179. package/dist/v3/queries.js +120 -0
  180. package/dist/v3/queries.js.map +1 -0
  181. package/dist/v3/segments.d.ts +29 -12
  182. package/dist/v3/segments.d.ts.map +1 -1
  183. package/dist/v3/segments.js +65 -23
  184. package/dist/v3/segments.js.map +1 -1
  185. package/dist/v3/syntax.d.ts +65 -14
  186. package/dist/v3/syntax.d.ts.map +1 -1
  187. package/dist/v3/syntax.js +167 -101
  188. package/dist/v3/syntax.js.map +1 -1
  189. package/package.json +10 -6
  190. package/src/client.ts +252 -0
  191. package/src/core/account.ts +86 -13
  192. package/src/core/amount.ts +3 -1
  193. package/src/core/balance.ts +6 -5
  194. package/src/core/bank-info.ts +1 -1
  195. package/src/core/client.ts +52 -15
  196. package/src/core/config.ts +69 -28
  197. package/src/core/errors.ts +122 -48
  198. package/src/core/feedback.ts +4 -1
  199. package/src/core/transaction.ts +9 -4
  200. package/src/core/transport.ts +98 -23
  201. package/src/formats/camt.ts +136 -39
  202. package/src/formats/mt940.ts +167 -81
  203. package/src/formats/readers.ts +24 -31
  204. package/src/formats/record-budget.ts +18 -0
  205. package/src/formats/sepa-purpose.ts +21 -2
  206. package/src/index.ts +4 -2
  207. package/src/session/app-approval.ts +51 -38
  208. package/src/session/dialog-runner.ts +31 -28
  209. package/src/session/login-guard.ts +174 -88
  210. package/src/session/sepa-accounts.ts +63 -45
  211. package/src/session/session-setup.ts +90 -71
  212. package/src/session/session-state.ts +56 -22
  213. package/src/session/signed-dialog.ts +192 -0
  214. package/src/session/tan-selection.ts +50 -39
  215. package/src/session/user-prompts.ts +35 -16
  216. package/src/tan/challenge.ts +40 -31
  217. package/src/tan/flicker.ts +44 -6
  218. package/src/tan/tan-image.ts +7 -5
  219. package/src/tan/tan-models.ts +10 -5
  220. package/src/util/async.ts +0 -5
  221. package/src/util/bytes.ts +13 -19
  222. package/src/util/call-context.ts +3 -2
  223. package/src/util/dates.ts +41 -24
  224. package/src/util/errors.ts +14 -3
  225. package/src/util/json.ts +16 -0
  226. package/src/util/text.ts +42 -12
  227. package/src/util/xml.ts +37 -26
  228. package/src/v3/account-extension.ts +7 -12
  229. package/src/v3/bank-connection.ts +67 -0
  230. package/src/v3/dialog.ts +175 -91
  231. package/src/v3/message.ts +69 -17
  232. package/src/v3/parameter-store.ts +160 -0
  233. package/src/v3/parameters.ts +110 -37
  234. package/src/v3/protocol.ts +70 -253
  235. package/src/v3/queries.ts +190 -0
  236. package/src/v3/segments.ts +65 -22
  237. package/src/v3/syntax.ts +153 -91
  238. package/dist/session/bank-connection.d.ts +0 -28
  239. package/dist/session/bank-connection.d.ts.map +0 -1
  240. package/dist/session/bank-connection.js +0 -48
  241. package/dist/session/bank-connection.js.map +0 -1
  242. package/dist/session/parameter-data.d.ts +0 -83
  243. package/dist/session/parameter-data.d.ts.map +0 -1
  244. package/dist/session/parameter-data.js +0 -68
  245. package/dist/session/parameter-data.js.map +0 -1
  246. package/dist/session/pin-tan-client.d.ts +0 -54
  247. package/dist/session/pin-tan-client.d.ts.map +0 -1
  248. package/dist/session/pin-tan-client.js +0 -153
  249. package/dist/session/pin-tan-client.js.map +0 -1
  250. package/dist/session/pin-tan-dialog.d.ts +0 -71
  251. package/dist/session/pin-tan-dialog.d.ts.map +0 -1
  252. package/dist/session/pin-tan-dialog.js +0 -124
  253. package/dist/session/pin-tan-dialog.js.map +0 -1
  254. package/dist/session/protocol.d.ts +0 -209
  255. package/dist/session/protocol.d.ts.map +0 -1
  256. package/dist/session/protocol.js +0 -75
  257. package/dist/session/protocol.js.map +0 -1
  258. package/dist/session/response-handler.d.ts +0 -48
  259. package/dist/session/response-handler.d.ts.map +0 -1
  260. package/dist/session/response-handler.js +0 -121
  261. package/dist/session/response-handler.js.map +0 -1
  262. package/dist/v3/client.d.ts +0 -24
  263. package/dist/v3/client.d.ts.map +0 -1
  264. package/dist/v3/client.js +0 -16
  265. package/dist/v3/client.js.map +0 -1
  266. package/src/session/bank-connection.ts +0 -51
  267. package/src/session/parameter-data.ts +0 -137
  268. package/src/session/pin-tan-client.ts +0 -186
  269. package/src/session/pin-tan-dialog.ts +0 -160
  270. package/src/session/protocol.ts +0 -311
  271. package/src/session/response-handler.ts +0 -130
  272. package/src/v3/client.ts +0 -29
@@ -1,311 +0,0 @@
1
- import type { Account } from '../core/account.js';
2
- import type { Balance } from '../core/balance.js';
3
- import type { BankMessage } from '../core/bank-info.js';
4
- import { requirePinLength, type FinTsConfig } from '../core/config.js';
5
- import { FinTsBankError, FinTsProtocolError, FinTsUnsupportedError } from '../core/errors.js';
6
- import type { FinTsFeedback } from '../core/feedback.js';
7
- import type { Transaction, TransactionFormat } from '../core/transaction.js';
8
- import type { TanChallenge } from '../tan/challenge.js';
9
- import type { TanMedium, TanMethod } from '../tan/tan-models.js';
10
- import type { LoginGuard } from './login-guard.js';
11
- import { knownAccount, lists, type BankParameters, type UserParameters } from './parameter-data.js';
12
-
13
- /**
14
- * The filler the specification names for a reference that does not exist.
15
- *
16
- * The bank puts it into a dummy answer when no approval is needed. The client sends it as the TAN medium name in the
17
- * dialog that looks up the TAN media, before it knows a medium; the bank ignores it there (PIN/TAN B.4.3.1.3).
18
- */
19
- export const noReference = 'noref';
20
-
21
- /**
22
- * A business transaction by its name, such as `HKCAZ`, and the version to send; `undefined` where the bank does not
23
- * offer it or the parameter data do not permit it.
24
- */
25
- export interface OfferedTransaction {
26
- readonly name: string;
27
- readonly version: number | undefined;
28
- }
29
-
30
- /**
31
- * What to request for transactions: camt.052 (`camt`) or MT940, the `version` to send, the `camtFormats` to name, and
32
- * the `currency` of statements that state none.
33
- */
34
- export interface TransactionsRequest {
35
- readonly camt: boolean;
36
- readonly version: number;
37
- readonly camtFormats: readonly string[];
38
- readonly currency: string | undefined;
39
- }
40
-
41
- /**
42
- * What to request for the transactions of `account` in `format`.
43
- *
44
- * The client reads camt.052 only, so camt counts as offered only if `camtFormats` include it. With `auto` the client
45
- * takes camt where the bank offers it, unless the user parameter data `upd` list only MT940 transactions for the
46
- * account.
47
- *
48
- * Throws `FinTsUnsupportedError` if the bank does not offer the chosen format for the account.
49
- */
50
- export function chooseTransactions(
51
- format: TransactionFormat,
52
- account: Account,
53
- upd: UserParameters | undefined,
54
- offered: { camt: OfferedTransaction; camtFormats: readonly string[]; mt940: OfferedTransaction },
55
- ): TransactionsRequest {
56
- const readable = offered.camtFormats.filter((f) => f.includes('camt.052'));
57
- const camtVersion = readable.length === 0 ? undefined : offered.camt.version;
58
- const known = knownAccount(upd, account);
59
- const useCamt =
60
- format === 'camt' ||
61
- (format === 'auto' &&
62
- camtVersion !== undefined &&
63
- (lists(known, offered.camt.name) || !lists(known, offered.mt940.name)));
64
- const version = useCamt ? camtVersion : offered.mt940.version;
65
- if (version === undefined) {
66
- const name = useCamt ? offered.camt.name : offered.mt940.name;
67
- throw new FinTsUnsupportedError(
68
- `The bank offers no ${useCamt ? 'camt ' : ''}transactions (${name}) for this account`,
69
- );
70
- }
71
- return { camt: useCamt, version, camtFormats: readable, currency: known.currency ?? account.currency };
72
- }
73
-
74
- /**
75
- * The query for the balance of `account`.
76
- *
77
- * `order` builds the request for each page. Once all pages are in, `records` collects the balance records of all pages,
78
- * `balanceRecord` picks the one for `account` with the help of `accountOf`, and `read` turns it into the `Balance`.
79
- */
80
- export function balanceQuery<R extends BankResponse, O, T>(
81
- account: Account,
82
- parts: {
83
- order: (continuation: string | undefined) => O;
84
- records: (pages: readonly R[]) => T[];
85
- accountOf: (record: T) => Account;
86
- read: (record: T, account: Account) => Balance;
87
- },
88
- ): Query<R, O, Balance> {
89
- return {
90
- order: parts.order,
91
- read: (pages) =>
92
- parts.read(
93
- balanceRecord(
94
- parts.records(pages),
95
- account,
96
- parts.accountOf,
97
- pages.flatMap((p) => p.feedback),
98
- ),
99
- account,
100
- ),
101
- };
102
- }
103
-
104
- /**
105
- * Checks that a message signed with `pin` may go out.
106
- *
107
- * It may not if the bank refused a login before (`LoginGuard.check`), or if the bank parameters `bpd` rule out the
108
- * PIN's length: the bank would count such a PIN as a failed attempt.
109
- */
110
- export function checkBeforeSigning(guard: LoginGuard, pin: string, bpd: BankParameters | undefined): void {
111
- guard.check();
112
- requirePinLength(pin, bpd?.pinLength.min, bpd?.pinLength.max);
113
- }
114
-
115
- /**
116
- * The balance record of `account` in `records`: the one whose account matches. A single record is taken as it is,
117
- * since the query asked for this one account only.
118
- *
119
- * Throws `FinTsBankError` with the bank's `feedback` if there is none, and `FinTsProtocolError` if there are several
120
- * and none matches.
121
- */
122
- export function balanceRecord<T>(
123
- records: readonly T[],
124
- account: Account,
125
- accountOf: (record: T) => Account,
126
- feedback: readonly FinTsFeedback[],
127
- ): T {
128
- if (records.length === 0) throw new FinTsBankError(feedback, 'No balance returned');
129
- if (records.length === 1) return records[0]!;
130
- const match = records.find((record) => accountOf(record).matches(account));
131
- if (match === undefined) {
132
- throw new FinTsProtocolError('The bank returned balances, but none for the requested account');
133
- }
134
- return match;
135
- }
136
-
137
- /** A bank response, reduced to what the protocol-independent session logic needs. */
138
- export interface BankResponse {
139
- /** Return codes of the whole message and of its parts. */
140
- readonly feedback: readonly FinTsFeedback[];
141
-
142
- /** Whether the bank rejected the message as a whole (an error in HIRMG), rather than single orders in it. */
143
- readonly messageRejected: boolean;
144
-
145
- /** Free-text messages the bank addresses to the user. */
146
- readonly bankMessages: readonly BankMessage[];
147
-
148
- /** The length of the message in bytes, a measure of the memory it holds. */
149
- readonly size: number;
150
- }
151
-
152
- /** The bank's request to approve a message with a TAN or in a banking app. */
153
- export interface Approval<R> {
154
- /** What the user is asked. */
155
- readonly challenge: TanChallenge;
156
-
157
- /** Sends the TAN the user entered. */
158
- readonly submitTan: (tan: string) => Promise<R>;
159
-
160
- /** Asks the bank whether the user has approved in the banking app. */
161
- readonly checkStatus: () => Promise<R>;
162
-
163
- /**
164
- * Whether the bank announces a push message for once the user has approved (return code 3957), before which the
165
- * status must not be asked. The client has no push channel, so the user reports the approval instead.
166
- */
167
- readonly waitForPush: boolean;
168
-
169
- /** The new instructions a status answer gives the user (return code 3956); `undefined` where it gives none. */
170
- readonly instructionsIn: (response: R) => string | undefined;
171
- }
172
-
173
- /** One message exchange of a dialog, sent when called. */
174
- export type Exchange<R> = () => Promise<R>;
175
-
176
- /**
177
- * The messages that open a dialog and send an order: `login` opens the dialog, and `order` sends the order in a message
178
- * of its own.
179
- */
180
- export interface StartWithOrder<R> {
181
- readonly login: Exchange<R>;
182
- readonly order: Exchange<R> | undefined;
183
- }
184
-
185
- /** What the handling of responses needs of a dialog, whatever its protocol. */
186
- export interface DialogHandle<R extends BankResponse> {
187
- /** The TAN method the dialog's messages are signed with. */
188
- readonly method: TanMethod;
189
-
190
- /** Whether the bank knows this dialog and it has not ended. */
191
- readonly isActive: boolean;
192
-
193
- /**
194
- * The approval `response`, the dialog's latest answer, asks for with return code 0030 (TAN) or 3955 (banking app);
195
- * `undefined` if it asks for none.
196
- *
197
- * The bank has not executed the message then, so a request without a challenge is a `FinTsProtocolError`: read as
198
- * done, it would report an empty result.
199
- */
200
- approvalAskedIn(response: R): Approval<R> | undefined;
201
-
202
- /** Forgets the dialog without contacting the bank, for example after the bank rejected the PIN. */
203
- discard(): void;
204
-
205
- /**
206
- * Ends the dialog at the bank; does nothing if it is not active.
207
- *
208
- * Bank and connection errors are ignored, since the bank may have ended the dialog on its side already.
209
- */
210
- end(): Promise<void>;
211
- }
212
-
213
- /** One dialog with the bank: a numbered sequence of messages under one dialog ID. */
214
- export interface ProtocolDialog<R extends BankResponse, O> extends DialogHandle<R> {
215
- /** Opens the dialog; with `synchronize` the bank also assigns a customer system ID. */
216
- start(synchronize?: boolean): Promise<R>;
217
-
218
- /** The messages that open the dialog and send `order` while no TAN medium is chosen yet, as for listing the media. */
219
- startWith(order: O): StartWithOrder<R>;
220
-
221
- /** Sends `order`, asking for strong customer authentication where the bank requires it. */
222
- send(order: O): Promise<R>;
223
- }
224
-
225
- /** A business transaction: the order to send for each page, and how to read all pages the bank sent. */
226
- export interface Query<R, O, T> {
227
- /** The order for the page at `continuation`; `undefined` for the first page. */
228
- readonly order: (continuation: string | undefined) => O;
229
-
230
- /** Reads the result; throws `FormatError` for malformed data. */
231
- readonly read: (pages: readonly R[]) => T;
232
- }
233
-
234
- /**
235
- * What the regular dialogs still need from a synchronization, a login in a dialog of its own, before the TAN method and
236
- * medium are settled (`TanSelection.synchronizationDialog` tells which method signs it):
237
- * - `none`: nothing, so no synchronization runs;
238
- * - `helpful`: nothing they cannot get themselves, but it tells the TAN methods the user may use before the user
239
- * chooses one, so it runs once per client;
240
- * - `required`: the customer system ID, without which no regular dialog starts (FinTS 3.0).
241
- */
242
- export type Synchronization = 'none' | 'helpful' | 'required';
243
-
244
- /** What the handling of responses needs of a protocol, whatever the orders it builds. */
245
- export interface ResponseProtocol<R extends BankResponse> {
246
- /** Keeps the parameter data and IDs in `response`, the answer to a message of a logged-in dialog. */
247
- keepParameterData(response: R): void;
248
-
249
- /** Joins a response with the one completing its approval. */
250
- combine(first: R, second: R): R;
251
- }
252
-
253
- /**
254
- * The protocol-specific half of a PIN/TAN client: builds and reads the messages.
255
- *
256
- * `PinTanClient` holds the other half, the session logic.
257
- */
258
- export interface PinTanProtocol<R extends BankResponse, O> extends ResponseProtocol<R> {
259
- /** Where and as whom the client talks to the bank. */
260
- readonly config: FinTsConfig;
261
-
262
- /** The bank parameter data received so far, if any. */
263
- readonly bankParameters: BankParameters | undefined;
264
-
265
- /** The user parameter data received so far, if any. */
266
- readonly userParameters: UserParameters | undefined;
267
-
268
- /** What regular dialogs still need from a synchronization dialog. */
269
- readonly synchronization: Synchronization;
270
-
271
- /**
272
- * Drops the customer system ID the bank no longer accepts, so that a synchronization is required again; returns
273
- * whether there was one.
274
- */
275
- forgetSystemId(): boolean;
276
-
277
- /** Keeps the PIN from going out after a refused login: the protocol checks it before every message it signs. */
278
- readonly loginGuard: LoginGuard;
279
-
280
- /** Requests the bank parameter data without logging in, and keeps them. */
281
- fetchBankParametersAnonymously(): Promise<R>;
282
-
283
- /** A new dialog signed with `method` and, where the method needs one, `tanMedium`. */
284
- newDialog(method: TanMethod, tanMedium?: string): ProtocolDialog<R, O>;
285
-
286
- /** Lists the TAN media; `undefined` if the bank does not offer it. */
287
- tanMedia(): Query<R, O, TanMedium[]> | undefined;
288
-
289
- /** Lists IBAN and BIC of the accounts; `undefined` if the bank does not offer it. */
290
- sepaAccounts(): Query<R, O, Account[]> | undefined;
291
-
292
- /** The balance of `account`; throws `FinTsUnsupportedError` if the bank does not report balances. */
293
- balance(account: Account): Query<R, O, Balance>;
294
-
295
- /** The transactions of `account`; throws `FinTsUnsupportedError` if the bank does not offer `format`. */
296
- transactions(
297
- account: Account,
298
- format: TransactionFormat,
299
- from: Date | undefined,
300
- to: Date | undefined,
301
- ): Query<R, O, Transaction[]>;
302
-
303
- /** The parameter data and IDs the protocol keeps, as JSON data, for the client's saved state. */
304
- saveState(): Record<string, unknown>;
305
-
306
- /** Restores what `saveState` saved; throws if `saved` is damaged, leaving the protocol unchanged. */
307
- restoreState(saved: Record<string, unknown>): void;
308
-
309
- /** Releases the connection. */
310
- close(): void;
311
- }
@@ -1,130 +0,0 @@
1
- import { bankErrorFor, FinTsLoginDataChangedError, FinTsTanCancelledError } from '../core/errors.js';
2
- import {
3
- allowedTanMethodsIn,
4
- FeedbackCodes,
5
- hasCode,
6
- hasErrors,
7
- userWarnings,
8
- type FinTsFeedback,
9
- } from '../core/feedback.js';
10
- import { invalidArgument } from '../util/errors.js';
11
- import { appApprovalWait } from './app-approval.js';
12
- import type { LoginGuard, RequestKind } from './login-guard.js';
13
- import type { Approval, BankResponse, DialogHandle, Exchange, ResponseProtocol, StartWithOrder } from './protocol.js';
14
- import type { SessionState } from './session-state.js';
15
- import type { UserPrompts } from './user-prompts.js';
16
-
17
- /** Checks what the bank answers and completes the approvals it asks for. The `LoginGuard` judges what errors mean. */
18
- export class ResponseHandler<R extends BankResponse> {
19
- constructor(
20
- private readonly protocol: ResponseProtocol<R>,
21
- private readonly state: SessionState,
22
- private readonly prompts: UserPrompts,
23
- private readonly guard: LoginGuard,
24
- ) {}
25
-
26
- /** Sends `login`, the exchange that opens `dialog`, and completes the approval the bank asks for. */
27
- startDialog(dialog: DialogHandle<R>, login: Exchange<R>): Promise<R> {
28
- return this.start(dialog, login, 'login');
29
- }
30
-
31
- /**
32
- * Opens `dialog` with `start`, which also sends an order, and returns the answers: the login's, then the order's where
33
- * the order has a message of its own. Completes the approval of each before the next.
34
- */
35
- async startDialogWith(dialog: DialogHandle<R>, start: StartWithOrder<R>): Promise<R[]> {
36
- const order = start.order;
37
- if (order === undefined) return [await this.start(dialog, start.login, 'loginWithOrder')];
38
- const login = await this.startDialog(dialog, start.login);
39
- return [login, await this.complete(dialog, await order())];
40
- }
41
-
42
- /** Receives `response`, the answer to an order in `dialog`, and completes the approval the bank asks for. */
43
- complete(dialog: DialogHandle<R>, response: R): Promise<R> {
44
- return this.settle(dialog, response, 'order');
45
- }
46
-
47
- /**
48
- * Receives `response`, the answer to a request of `kind` in `dialog`: keeps its parameter data, even alongside errors,
49
- * since the remedy for a refused login may need them; passes its notices on; and throws its errors once the guard has
50
- * judged them (`LoginGuard.judge`).
51
- */
52
- receive(dialog: DialogHandle<R>, response: R, kind: RequestKind): void {
53
- this.protocol.keepParameterData(response);
54
- this.readNotices(response);
55
- if (!hasErrors(response.feedback)) return;
56
- const error = bankErrorFor(response.feedback, kind === 'approval');
57
- // Every further message of a refused login's dialog would carry the refused PIN.
58
- if (this.guard.judge(error, kind, dialog.isActive, dialog.method)) dialog.discard();
59
- throw error;
60
- }
61
-
62
- /**
63
- * Passes the bank's messages in `response` to the user: all an anonymous dialog tells, since the TAN methods it lists
64
- * are not those of a user.
65
- */
66
- showBankMessages(response: R): void {
67
- for (const message of response.bankMessages) this.prompts.showBankMessage(message);
68
- }
69
-
70
- private async start(dialog: DialogHandle<R>, login: Exchange<R>, kind: RequestKind): Promise<R> {
71
- const response = await this.settle(dialog, await login(), kind);
72
- if (hasCode(response.feedback, FeedbackCodes.newLoginData)) await this.retireLoginData(dialog, response.feedback);
73
- return response;
74
- }
75
-
76
- /**
77
- * Ends `dialog`, whose login brought new login data, and stops the client from logging in with the old ones
78
- * (Rückmeldungscodes C.6).
79
- */
80
- private async retireLoginData(dialog: DialogHandle<R>, feedback: readonly FinTsFeedback[]): Promise<never> {
81
- await dialog.end();
82
- const error = new FinTsLoginDataChangedError(feedback);
83
- this.guard.refuse(error);
84
- throw error;
85
- }
86
-
87
- /**
88
- * Receives `response`, the answer to a request of `kind`, then completes the approval it asks for; returns it joined
89
- * with the response that completes the approval.
90
- */
91
- private async settle(dialog: DialogHandle<R>, response: R, kind: RequestKind): Promise<R> {
92
- this.receive(dialog, response, kind);
93
- const approval = dialog.approvalAskedIn(response);
94
- if (approval === undefined) return response;
95
- const approved = approval.challenge.isDecoupled
96
- ? await this.awaitAppApproval(dialog, approval)
97
- : await this.enterTan(dialog, approval);
98
- return this.protocol.combine(response, approved);
99
- }
100
-
101
- /** Passes on the notices in `response`: the TAN methods the user may use (3920), the bank's messages and warnings. */
102
- private readNotices(response: R): void {
103
- const allowed = allowedTanMethodsIn(response.feedback);
104
- if (allowed.length > 0) this.state.rememberAllowedMethods(allowed);
105
- this.showBankMessages(response);
106
- for (const warning of userWarnings(response.feedback)) this.prompts.showWarning(warning);
107
- }
108
-
109
- private async enterTan(dialog: DialogHandle<R>, approval: Approval<R>): Promise<R> {
110
- const answer = await this.prompts.askTan(approval.challenge);
111
- if (answer.kind === 'approved') {
112
- throw invalidArgument('Answered a TAN challenge with an approval in the banking app', 'tanCallback');
113
- }
114
- const tan = answer.kind === 'tan' ? answer.tan.trim() : '';
115
- if (tan === '') throw new FinTsTanCancelledError();
116
- const response = await approval.submitTan(tan);
117
- this.receive(dialog, response, 'approval');
118
- return response;
119
- }
120
-
121
- private awaitAppApproval(dialog: DialogHandle<R>, approval: Approval<R>): Promise<R> {
122
- return appApprovalWait(approval, {
123
- ask: (challenge) => this.prompts.askTan(challenge),
124
- pause: (delayMs, interrupt) => this.prompts.pause(delayMs, interrupt),
125
- receive: (response) => {
126
- this.receive(dialog, response, 'approval');
127
- },
128
- }).run();
129
- }
130
- }
package/src/v3/client.ts DELETED
@@ -1,29 +0,0 @@
1
- import type { FinTsClientOptions } from '../core/client.js';
2
- import { FinTsConfig, type FinTsConfigInit } from '../core/config.js';
3
- import { PinTanClient } from '../session/pin-tan-client.js';
4
- import type { IncomingMessage3 } from './message.js';
5
- import { FinTs3Protocol } from './protocol.js';
6
- import type { SegmentSpec } from './syntax.js';
7
-
8
- /** What `FinTs3Client` takes. */
9
- export interface FinTs3ClientInit {
10
- /** Where and as whom to log in: a `FinTsConfig`, or what its constructor takes. */
11
- config: FinTsConfig | FinTsConfigInit;
12
- /** The user's PIN; the client keeps it in memory only and never logs it. */
13
- pin: string;
14
- /** How the client reaches the user and the bank. */
15
- options: FinTsClientOptions;
16
- }
17
-
18
- /** FinTS 3.0 PIN/TAN client, the version German banks use. */
19
- export class FinTs3Client extends PinTanClient<IncomingMessage3, SegmentSpec> {
20
- /**
21
- * Throws `TypeError` if the configuration is invalid, if the PIN is empty, or if the PIN or login data contain
22
- * characters FinTS 3.0 cannot send: it uses ISO 8859-1, so `€` and most non-Latin letters are not possible. Throws as
23
- * well for a product ID longer than 25 or a product version longer than 5 characters.
24
- */
25
- constructor(init: FinTs3ClientInit) {
26
- const config = init.config instanceof FinTsConfig ? init.config : new FinTsConfig(init.config);
27
- super(init.options, (logger) => new FinTs3Protocol(config, init.pin, init.options.transport, logger));
28
- }
29
- }