Conversation
- Add v4/ module with XML builder, parser, camt.053 parser - Add FinTS4Client with read-only operations (accounts, balance, statements) - Add FinTS4Dialog for v4.1 dialog lifecycle - Add NegotiatingClient for automatic version negotiation - Export new modules from index.ts Agent-Logs-Url: https://github.com/larsdecker/fints/sessions/5059b3ac-a6a0-4446-ab2f-e0d0614e57f2 Co-authored-by: larsdecker <1968186+larsdecker@users.noreply.github.com>
- Fix XML parser parseTagValue to preserve string codes like "0010" - Fix camt parser isArray config for proper nested path resolution - Add maxRetries/retryDelay to FinTS4ClientConfig - Fix dialog and negotiating client tests Agent-Logs-Url: https://github.com/larsdecker/fints/sessions/5059b3ac-a6a0-4446-ab2f-e0d0614e57f2 Co-authored-by: larsdecker <1968186+larsdecker@users.noreply.github.com>
- Add comprehensive spec-conformant tests covering: - §B.5: Return codes (0xxx, 3xxx, 9xxx) as defined in specification - §B.3: Message envelope structure (MsgHead, MsgBody, MsgTail) - §B.4: Security profile (PIN/TAN with SignatureHeader/SignatureTrailer) - §C.1: Dialog lifecycle (Sync → Init → Business → End) - §D.2: TAN method negotiation (decoupled, process 2/4) - §D.3: Account statements with realistic camt.053 (ISO 20022) - §B.6: Response parsing scenarios (BPD, UPD, touchdown) - Add NegotiatingClient integration tests for v4.1 routing - All 507 tests pass, v4 code has >97% statement/line coverage Agent-Logs-Url: https://github.com/larsdecker/fints/sessions/5059b3ac-a6a0-4446-ab2f-e0d0614e57f2 Co-authored-by: larsdecker <1968186+larsdecker@users.noreply.github.com>
Agent-Logs-Url: https://github.com/larsdecker/fints/sessions/5059b3ac-a6a0-4446-ab2f-e0d0614e57f2 Co-authored-by: larsdecker <1968186+larsdecker@users.noreply.github.com>
…de review Agent-Logs-Url: https://github.com/larsdecker/fints/sessions/5059b3ac-a6a0-4446-ab2f-e0d0614e57f2 Co-authored-by: larsdecker <1968186+larsdecker@users.noreply.github.com>
- MockBankServer: simulated FinTS 4.1 bank server with realistic test data - Supports full dialog lifecycle, authentication, account list, balance, statements - Realistic German banking test data (salary, rent, utilities, freelance income) - camt.053 ISO 20022 test data with proper counterparty and reference information - 21 integration tests covering authentication, sync, lifecycle, CRUD operations - All 528 tests pass Agent-Logs-Url: https://github.com/larsdecker/fints/sessions/5059b3ac-a6a0-4446-ab2f-e0d0614e57f2 Co-authored-by: larsdecker <1968186+larsdecker@users.noreply.github.com>
- MockBankServerV3: simulated FinTS 3.0 bank server with realistic test data - Supports dialog lifecycle, authentication, HISPA, HISAL, HIKAZ - MT940 test data generator for account statements - Integration tests started but HIRMS segment format needs adjustment Agent-Logs-Url: https://github.com/larsdecker/fints/sessions/5059b3ac-a6a0-4446-ab2f-e0d0614e57f2 Co-authored-by: larsdecker <1968186+larsdecker@users.noreply.github.com>
- fints-lib: 0.10.0 → 0.11.0 (FinTS 4.1 support, mock bank servers) - fints-cli: 0.5.0 → 0.6.0 Agent-Logs-Url: https://github.com/larsdecker/fints/sessions/be341dbf-f9f7-409d-8e16-32000dc91a7c Co-authored-by: larsdecker <1968186+larsdecker@users.noreply.github.com>
Two bugs fixed in MockBankServerV3: 1. buildSegment: remove spurious trailing '+' before the segment terminator "'" — the empty trailing data group caused HIRMS.deserialize to receive undefined for the 'references' field, crashing all tests that called sync(). 2. buildTanMethodsSegment: rewrite to match the HITANS v7 wire format: - All TAN methods must live in a single '+'-separated DEG (colon-delimited) - The DEG starts with oneStepAllowed:multiple:securityProfile - Per-method fields must follow the 30-field v7 order from tanMethodArgumentMap All 13 FinTS 3.0 integration tests now pass (were 2/13 before). Agent-Logs-Url: https://github.com/larsdecker/fints/sessions/be341dbf-f9f7-409d-8e16-32000dc91a7c Co-authored-by: larsdecker <1968186+larsdecker@users.noreply.github.com>
Agent-Logs-Url: https://github.com/larsdecker/fints/sessions/b71e69c3-f9e4-4a21-95b2-a71703d2a2f0 Co-authored-by: larsdecker <1968186+larsdecker@users.noreply.github.com>
- Remove unused imports: FinTS4ClientConfig, parse, FinTS4Response, TEST_USERS, FinTS4Client/PinTanClient (test-negotiating-client-v4), FinTSProtocolVersion, mockFetchOk, buildDialogEndSegment/buildSyncSegment/ buildBalanceSegment/FinTS4Client (test-spec-conformance), getXmlNumber, SEPAAccount/buildAccountListSegment (dialog.ts), Statement/BankCapabilities (types.ts) - Run prettier --write on all modified files - eslint: 0 errors, 0 warnings Agent-Logs-Url: https://github.com/larsdecker/fints/sessions/b71e69c3-f9e4-4a21-95b2-a71703d2a2f0 Co-authored-by: larsdecker <1968186+larsdecker@users.noreply.github.com>
…entation Agent-Logs-Url: https://github.com/larsdecker/fints/sessions/f5c85e95-97df-4d1f-8153-8bfbc292ee4a Co-authored-by: larsdecker <1968186+larsdecker@users.noreply.github.com>
…s; fix TAN submit segment, version negotiation, TLS agent, exports Agent-Logs-Url: https://github.com/larsdecker/fints/sessions/f5c85e95-97df-4d1f-8153-8bfbc292ee4a Co-authored-by: larsdecker <1968186+larsdecker@users.noreply.github.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c9614a8595
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // Parse camt data | ||
| const statementSegment = findSegment(msgBody, "AccountStatement"); | ||
| const camtData = statementSegment | ||
| ? getXmlString(getXmlValue(statementSegment, "SegBody") as Record<string, unknown>, "CamtData") | ||
| : undefined; |
There was a problem hiding this comment.
Parse balance segment in FinTS4 responses
FinTS4Client.balance() relies on response.balance, but parseResponse() never extracts a Balance segment, so response.balance is always undefined and the client silently returns the hardcoded zero-balance fallback even when the bank returned real balance values. This causes incorrect financial data for every successful balance query unless downstream code bypasses FinTS4Client.balance().
Useful? React with 👍 / 👎.
| xmlElement("CustomerID", escapeXml(options.name)) + | ||
| xmlElement("SystemID", escapeXml(options.systemId)) + | ||
| xmlElement("Product", xmlElement("Name", escapeXml(productId)) + xmlElement("Version", PRODUCT_VERSION)) + | ||
| xmlElement("HBCIVersion", FINTS_VERSION); |
There was a problem hiding this comment.
Use negotiated HBCI version in DialogInit segment
The sync retry logic negotiates hbciVersion (4.1 → 4.0 → 3.0), but buildDialogInitSegment() still hardcodes <HBCIVersion> to FINTS_VERSION (4.1). On servers that validate the version inside DialogInit, retries will keep advertising 4.1 in the segment body and fail with 9010, so version fallback cannot actually recover.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
Introduces an experimental FinTS 4.1 (XML-based) implementation alongside the existing FinTS 3.0 client, including a mock server and a negotiating client to select v3 vs v4 at runtime.
Changes:
- Added a FinTS 4.1 module (XML builder/parser, dialog/client/connection, segment builders, camt parser) plus extensive unit/integration tests.
- Added a FinTS 4.1 mock bank server + test data to enable realistic end-to-end integration testing.
- Added a
NegotiatingClientand updated public exports + documentation to expose the new v4 functionality.
Reviewed changes
Copilot reviewed 35 out of 36 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| yarn.lock | Lockfile updates for dependency resolution sources. |
| packages/fints/src/v4/xml-parser.ts | New FinTS 4.1 XML response parsing utilities + response parsing. |
| packages/fints/src/v4/xml-builder.ts | New FinTS 4.1 XML message builder. |
| packages/fints/src/v4/types.ts | New FinTS 4.1 public types (config/response/TAN types). |
| packages/fints/src/v4/test-server/test-data.ts | Mock server test fixtures (bank/users/accounts/camt). |
| packages/fints/src/v4/test-server/mock-bank-server.ts | New FinTS 4.1 mock server implementation. |
| packages/fints/src/v4/test-server/index.ts | Public exports for the v4 test server. |
| packages/fints/src/v4/segments/index.ts | New v4 segment builders incl. TAN submit segment. |
| packages/fints/src/v4/index.ts | Public exports for the v4 module. |
| packages/fints/src/v4/dialog.ts | New v4 dialog orchestration incl. TAN callback + version negotiation. |
| packages/fints/src/v4/constants.ts | New v4 protocol constants (namespace/version/camt). |
| packages/fints/src/v4/connection.ts | New v4 HTTP connection + TLS agent helper. |
| packages/fints/src/v4/client.ts | New v4 read-only client (accounts/balance/statements/capabilities). |
| packages/fints/src/v4/camt-parser.ts | New camt.052/053 parsing for v4 statement responses. |
| packages/fints/src/v4/tests/test-xml-parser.ts | Unit tests for v4 XML parsing helpers + response parsing. |
| packages/fints/src/v4/tests/test-xml-builder.ts | Unit tests for v4 XML message builder. |
| packages/fints/src/v4/tests/test-segments.ts | Unit tests for v4 segment builders. |
| packages/fints/src/v4/tests/test-negotiating-client.ts | Tests for negotiating client behavior (v3 default / v4 creation). |
| packages/fints/src/v4/tests/test-negotiating-client-v4.ts | Tests for negotiating strategy + routing when v4 is used. |
| packages/fints/src/v4/tests/test-mock-bank-integration.ts | End-to-end integration tests against the v4 mock server. |
| packages/fints/src/v4/tests/test-dialog.ts | Unit tests for v4 dialog lifecycle and behavior. |
| packages/fints/src/v4/tests/test-connection.ts | Unit tests for v4 HTTP connection retry/timeout behavior. |
| packages/fints/src/v4/tests/test-client.ts | Unit tests for v4 client high-level methods. |
| packages/fints/src/v4/tests/test-camt-parser.ts | Unit tests for camt parsing logic. |
| packages/fints/src/test-server/test-data.ts | Renames/organizes v3 mock-server test constants to avoid v4 collisions. |
| packages/fints/src/test-server/mock-bank-server.ts | Updates v3 mock server to match renamed fixtures and reset behavior. |
| packages/fints/src/test-server/index.ts | Updates v3 test-server public exports after renames. |
| packages/fints/src/test-server/tests/test-mock-bank-v3-integration.ts | Updates v3 integration tests to renamed fixtures. |
| packages/fints/src/negotiating-client.ts | Adds a unified client that selects FinTS 3.0 vs 4.1 at runtime. |
| packages/fints/src/index.ts | Exposes v4 module + negotiating client from the main package entrypoint. |
| packages/fints/package.json | Bumps core library version. |
| packages/fints/README.md | Adds experimental v4 docs, quickstart, TAN callback, TLS guidance, negotiation notes. |
| packages/fints-cli/package.json | Bumps CLI version. |
| eslint.config.js | Adds globals required by new code/tests (e.g. globalThis/require/module). |
| README.md | Updates root docs with experimental v4 guide, TLS and negotiation sections. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const productId = options.productId || PRODUCT_NAME; | ||
| const body = | ||
| xmlElement("BLZ", options.blz) + | ||
| xmlElement("CountryCode", COUNTRY_CODE) + | ||
| xmlElement("CustomerID", escapeXml(options.name)) + | ||
| xmlElement("SystemID", escapeXml(options.systemId)) + | ||
| xmlElement("Product", xmlElement("Name", escapeXml(productId)) + xmlElement("Version", PRODUCT_VERSION)) + | ||
| xmlElement("HBCIVersion", FINTS_VERSION); | ||
|
|
There was a problem hiding this comment.
buildDialogInitSegment always embeds FINTS_VERSION ("4.1") in the segment body. During HBCI version negotiation (e.g. retrying with "4.0"/"3.0"), this makes the DialogInit segment inconsistent with the negotiated hbciVersion used in the MsgHead and can cause servers to reject the request. Consider passing hbciVersion into the segment builder (or removing this field if it’s redundant) so it matches the actual version in use.
| isArray: (name: string) => { | ||
| // These elements can occur multiple times | ||
| return [ | ||
| "Segment", | ||
| "ReturnValue", | ||
| "Account", | ||
| "TANMethod", | ||
| "Version", | ||
| "Language", | ||
| "SecurityMethod", | ||
| "PainFormat", | ||
| "CamtFormat", | ||
| "Transaction", | ||
| "Entry", | ||
| "Ntry", | ||
| "Stmt", | ||
| ].includes(name); | ||
| }, |
There was a problem hiding this comment.
parserOptions.isArray does not include "Parameter", but parseReturnValues() expects multiple <Parameter> elements and calls ensureArray() on the parsed value. Without marking it as an array, fast-xml-parser may collapse multiple <Parameter> nodes, losing all but one parameter. Add "Parameter" to isArray (or switch to an ensureArray strategy that works even when the parser overwrites duplicates).
| constructor(config: FinTS4ClientConfig) { | ||
| this.config = config; | ||
| this.connection = new FinTS4HttpConnection({ | ||
| url: config.url, | ||
| debug: config.debug, | ||
| timeout: config.timeout, | ||
| maxRetries: config.maxRetries, | ||
| retryDelay: config.retryDelay, | ||
| fetchOptions: config.fetchOptions, | ||
| }); | ||
| } |
There was a problem hiding this comment.
FinTS4ClientConfig exposes tlsOptions, but the client currently never uses it (only fetchOptions is forwarded). This makes tlsOptions a no-op for consumers. Either remove tlsOptions from the public config or implement it by creating an agent via createTlsAgent() and wiring it into fetchOptions when running in Node.js.
| private createV4Client(config: NegotiatingClientConfig): FinTS4Client { | ||
| return new FinTS4Client({ | ||
| blz: config.blz, | ||
| name: config.name, | ||
| pin: config.pin, | ||
| url: config.v4Url || config.url, | ||
| productId: config.productId, | ||
| debug: config.debug, | ||
| timeout: config.timeout, | ||
| maxRetries: config.maxRetries, | ||
| retryDelay: config.retryDelay, | ||
| }); | ||
| } |
There was a problem hiding this comment.
NegotiatingClient creates the v4 client without forwarding v4-specific options like tanCallback, fetchOptions (TLS agent), or preferredHbciVersion. Also, NegotiatingClientConfig doesn’t currently allow specifying them. If NegotiatingClient is intended to be a unified entry point, consider extending its config to include these v4 options and passing them through when constructing FinTS4Client.
| For FinTS 3.0 (`PinTanClient`), pass the agent the same way: | ||
|
|
||
| ```typescript | ||
| import { PinTanClient, HttpConnection } from "fints-lib"; | ||
|
|
||
| const connection = new HttpConnection({ url: "https://...", agent }); | ||
| const client = new PinTanClient({ /* ... */ }, connection); | ||
| ``` | ||
|
|
There was a problem hiding this comment.
The README suggests passing an agent into the v3 HttpConnection/PinTanClient flow, but packages/fints/src/http-connection.ts’s ConnectionConfig doesn’t support an agent option and the fetch call doesn’t forward it. This snippet appears inaccurate; either update the v3 connection to support a custom agent or adjust the documentation to only describe agent usage for v4 (fetchOptions.agent).
| For FinTS 3.0 (`PinTanClient`), pass the agent the same way: | |
| ```typescript | |
| import { PinTanClient, HttpConnection } from "fints-lib"; | |
| const connection = new HttpConnection({ url: "https://...", agent }); | |
| const client = new PinTanClient({ /* ... */ }, connection); | |
| ``` | |
| For now, this custom agent configuration is only documented for the FinTS 4 flow via | |
| `fetchOptions.agent`. | |
| `PinTanClient` / `HttpConnection` (FinTS 3.0) do not currently expose the same documented | |
| `agent` option, so the `new HttpConnection({ url: "https://...", agent })` pattern should | |
| not be used here. |
| public async send(xmlRequest: string): Promise<string> { | ||
| verbose(`FinTS 4.1: Sending request to ${this.url}`); | ||
| if (this.debug) { | ||
| verbose(`FinTS 4.1 Request XML:\n${xmlRequest}`); | ||
| } |
There was a problem hiding this comment.
FinTS4HttpConnection logs full request/response XML when debug is enabled. That XML contains <PIN> and may contain <TAN>, which can leak credentials into logs. Consider redacting sensitive fields before logging (e.g., replace PIN/TAN element contents with "***") similar to how the v3 HttpConnection avoids logging the raw request string.
| export function createTlsAgent(options: FinTS4TlsOptions): unknown { | ||
| // eslint-disable-next-line @typescript-eslint/no-require-imports | ||
| const https = require("https") as typeof import("https"); | ||
| return new https.Agent({ | ||
| rejectUnauthorized: options.rejectUnauthorized ?? true, | ||
| ...(options.ca ? { ca: options.ca } : {}), | ||
| }); | ||
| } |
There was a problem hiding this comment.
createTlsAgent() is documented as throwing outside Node.js, but the current implementation will throw a generic ReferenceError: require is not defined in browser-like runtimes. Add an explicit environment check and throw a clearer error message (and/or use a safe dynamic import) so consumers get a predictable failure mode.
| if (response.balance) { | ||
| return response.balance; | ||
| } | ||
|
|
||
| // Build balance from raw response | ||
| return { | ||
| account, | ||
| availableBalance: 0, | ||
| bookedBalance: 0, | ||
| currency: "EUR", | ||
| creditLimit: 0, | ||
| pendingBalance: 0, | ||
| productName: "", | ||
| }; | ||
| } |
There was a problem hiding this comment.
balance() currently returns a hard-coded zeroed balance when the response doesn’t include response.balance, but parseResponse() never populates balance at all. As a result, FinTS4Client.balance() will always return zeros even when the server provides a Balance segment. Implement Balance segment parsing (either in parseResponse() or directly here using the Balance segment body) so callers get real balances.
| public reset(): void { | ||
| this.dialogs.clear(); | ||
| this.requestLog = []; | ||
| this.responseLog = []; | ||
| this.nextDialogId = 1000; | ||
| this.nextSystemId = 1; | ||
| } |
There was a problem hiding this comment.
reset() clears dialogs and logs, but handleSync() mutates TEST_USERS[...].systemId. Since TEST_USERS is module-level state, not resetting it can leak state between tests and make results order-dependent. Consider resetting all TEST_USERS[*].systemId values in reset() (similar to the v3 mock server).
| /** | ||
| * Callback invoked when the server issues a TAN challenge. | ||
| * If not provided and a TAN is required, a `TanRequiredError` is thrown. | ||
| */ | ||
| tanCallback?: TanCallback; |
There was a problem hiding this comment.
The tanCallback docs in v4 types mention throwing TanRequiredError, but the v4 dialog throws FinTS4TanRequiredError. Update the comment to reference the correct error type so consumers know what to catch.
|
@copilot apply changes based on the comments in this thread |
… DialogInit, Parameter array, tlsOptions wiring, TAN docstrings, log redaction, createTlsAgent check, NegotiatingClient v4 options, mock reset, README agent docs Agent-Logs-Url: https://github.com/larsdecker/fints/sessions/ed4d42e4-58c3-4403-a4c9-d64bd3f14ae6 Co-authored-by: larsdecker <1968186+larsdecker@users.noreply.github.com>
All review comments from both threads have been addressed in commit
|
fetchOptions+createTlsAgenthelper inconnection.ts)TanChallenge,TanCallback,FinTS4TanRequiredError, challenge detection + auto-resolution indialog.send())maxTransactionsPerMsg,minSignaturesBalance/Statement, fallback element names,negotiatedVersion)9010with4.1 → 4.0 → 3.0fallback,preferredHbciVersionconfig option,hbciVersionpropagated intobuildMessage)buildTanSubmitSegmentto segments, export fromv4/index.tsFinTS4TanRequiredError,createTlsAgent,TanChallenge,TanCallback,FinTS4TlsOptionsto public exportsREADME.md— prominent experimental warning, FinTS 4.1 quickstart, TLS agent guide, version negotiation sectionpackages/fints/README.md— experimental warning, FinTS 4.1 config reference, TAN callback example,FinTS4TanRequiredErrorusage