Skip to content

larsdecker/fints

Β 
Β 

Repository files navigation

fints-lib

npm CI

A client library for communicating with FinTS servers.

Note: This is a fork and continuation of the excellent work by Frederick Gnodtke (Prior99). We are grateful for the solid foundation and comprehensive implementation provided by the original project.


⚠️ FinTS 4.1 / FinTS 4.0 – Experimental Support

Experimental β€” use with caution in production

This library includes an implementation of the FinTS 4.1 XML-based protocol (FinTS4Client). This support is experimental and subject to the following limitations:

  • FinTS 4.0 is not a widely deployed version. Most German retail banks still use FinTS 3.0. FinTS 4.1 support is intended for banks and aggregators that have explicitly adopted the XML-based successor format.
  • Protocol negotiation is best-effort. The implementation will automatically try fallback versions (4.1 β†’ 4.0 β†’ 3.0) when a bank rejects the preferred version, but real-world servers may behave unpredictably.
  • TAN flows are partially supported. The interactive two-step TAN flow (PIN+TAN, chipTAN, pushTAN) is implemented via a tanCallback, but edge cases β€” such as HHD/Flickercode visualisation, multi-challenge flows, or bank-specific challenge formats β€” may require additional handling.
  • BPD/UPD parsing is partially generic. The FinTS 4.1 XML structure leaves room for bank-specific element naming. The parser applies fallback strategies, but untested banks may require further mapping.
  • No write operations. FinTS4Client is currently read-only (accounts, balances, statements). Credit transfers and direct debits are only available via the stable PinTanClient (FinTS 3.0).
  • TLS certificate requirements. Banks with private CA certificates or strict TLS policies require a custom Node.js https.Agent, which must be passed via fetchOptions. See Custom TLS / HTTPS configuration below.

For production use with the widest bank compatibility, use the PinTanClient (FinTS 3.0) described in the Quick Start section.


🎯 Improvements in this Fork

  • βœ… Updated Dependencies: All dependencies updated to their latest stable versions
  • βœ… Modern TypeScript: TypeScript 5.x with improved type safety
  • βœ… GitHub Actions: Automated CI/CD pipeline
  • βœ… FinTS 4.1 (Experimental): XML-based protocol with interactive TAN, version negotiation, and improved BPD parsing
  • βœ… Active Maintenance: Regular updates and dependency maintenance
  • βœ… Published as fints-lib and fints-lib-cli on npm

πŸ“¦ Installation

npm install fints-lib
# or
yarn add fints-lib

For the CLI tool:

npm install -g fints-lib-cli
# or
yarn global add fints-lib-cli

Development Setup

# Install dependencies
yarn install

# Build all packages
yarn build

# Run tests
yarn test

# Run linting
yarn lint

πŸš€ Quick Start

FinTS 3.0 (Stable β€” recommended for production)

import { PinTanClient } from "fints-lib";

const client = new PinTanClient({
    url: "https://banking.example.com/fints", // Your bank's FinTS URL
    name: "username",
    pin: "12345",
    blz: "12345678",
});

const accounts = await client.accounts();
console.log(accounts);

FinTS 4.1 (Experimental β€” XML-based)

import { FinTS4Client } from "fints-lib";

// ⚠️ Experimental: most banks still use FinTS 3.0
const client = new FinTS4Client({
    url: "https://banking.example.com/fints41",
    name: "username",
    pin: "12345",
    blz: "12345678",
});

const accounts = await client.accounts();
const balance  = await client.balance(accounts[0]);
const stmts    = await client.camtStatements(accounts[0]);

CLI Quick Start

npm install -g fints-lib-cli

fints-lib list-accounts \
  --url https://banking.example.com/fints \
  -n username -p 12345 -b 12345678

πŸ“– Common Use Cases

1. Check Account Balance

import { PinTanClient } from "fints-lib";

const client = new PinTanClient({
    url: process.env.FINTS_URL,
    name: process.env.FINTS_USERNAME,
    pin: process.env.FINTS_PIN,
    blz: process.env.FINTS_BLZ,
});

const accounts = await client.accounts();
const balance  = await client.balance(accounts[0]);
console.log(`Balance: ${balance.value.value} ${balance.value.currency}`);

2. Fetch Recent Transactions

import { PinTanClient } from "fints-lib";

const client = new PinTanClient({ /* ... */ });
const accounts = await client.accounts();

const endDate   = new Date();
const startDate = new Date(endDate.getTime() - 30 * 24 * 60 * 60 * 1000);
const statements = await client.statements(accounts[0], startDate, endDate);

statements.forEach((statement) => {
    statement.transactions.forEach((tx) => {
        console.log(`  ${tx.amount} ${tx.currency} β€” ${tx.purpose || "N/A"}`);
    });
});

3. SEPA Credit Transfer (Send Money)

import { PinTanClient, TanRequiredError } from "fints-lib";

const client = new PinTanClient({ /* ... */ });
const accounts = await client.accounts();

try {
    await client.creditTransfer(accounts[0], {
        debtorName: "John Doe",
        creditor: { name: "Recipient", iban: "DE44500105175407324931", bic: "INGDDEFFXXX" },
        amount: 50.0,
        remittanceInformation: "Invoice #12345",
    });
} catch (error) {
    if (error instanceof TanRequiredError) {
        const tan = await promptUser(error.challengeText); // your UI
        await client.completeCreditTransfer(error.dialog, error.transactionReference, tan, error.creditTransferSubmission);
    }
}

4. FinTS 4.1 β€” Interactive TAN (Experimental)

⚠️ Experimental. Requires a bank that supports FinTS 4.1 XML.

import { FinTS4Client } from "fints-lib";
import * as readline from "readline";

async function promptTan(challenge: { challengeText?: string; transactionReference: string }): Promise<string> {
    const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
    return new Promise((resolve) => {
        rl.question(`TAN challenge: ${challenge.challengeText ?? "(no text)"}\nEnter TAN: `, (tan) => {
            rl.close();
            resolve(tan.trim());
        });
    });
}

const client = new FinTS4Client({
    url: "https://banking.example.com/fints41",
    name: "username",
    pin: "12345",
    blz: "12345678",
    tanCallback: promptTan, // called automatically when the bank issues a challenge
});

const accounts = await client.accounts();
// If the bank requires a TAN during the request, `promptTan` is invoked automatically.
const statements = await client.camtStatements(accounts[0]);

πŸ” Custom TLS / HTTPS Configuration (Node.js only)

Some banks use certificates signed by a private CA, or require specific TLS settings. Use createTlsAgent() to create a custom https.Agent and pass it via fetchOptions:

import { FinTS4Client, createTlsAgent } from "fints-lib";
import fs from "fs";

// Use a custom CA certificate (e.g. a bank-specific private CA)
const agent = createTlsAgent({
    ca: fs.readFileSync("/path/to/bank-ca.pem", "utf8"),
});

const client = new FinTS4Client({
    url: "https://banking.example.com/fints41",
    name: "username",
    pin: "12345",
    blz: "12345678",
    fetchOptions: { agent },
});

Security note: Never set rejectUnauthorized: false in production. This disables certificate verification entirely and exposes you to man-in-the-middle attacks.

// βœ… Development / testing only:
const devAgent = createTlsAgent({ rejectUnauthorized: false });

// ❌ Never in production:
// const prodAgent = createTlsAgent({ rejectUnauthorized: false });

Note: createTlsAgent() and fetchOptions.agent are currently only supported by FinTS4Client (FinTS 4.1). The FinTS 3.0 PinTanClient / HttpConnection does not expose a custom-agent option; if you need custom TLS for a FinTS 3.0 endpoint, use the NegotiatingClient with preferredVersion: "4.1" or configure TLS at the Node.js process level (e.g. NODE_EXTRA_CA_CERTS).

πŸ”„ HBCI Version Negotiation (FinTS 4.1)

FinTS4Client automatically negotiates the HBCI version with the server:

  1. It starts with your preferredHbciVersion (default: "4.1").
  2. If the server returns error 9010 (version not supported), it retries with "4.0", then "3.0".
  3. After the sync phase, the client adopts the highest version both sides support.
const client = new FinTS4Client({
    // ...
    preferredHbciVersion: "4.1", // start with 4.1, fall back automatically
});

Packages

πŸ”’ Security

  • Never log credentials. The library masks PINs and TANs in debug output, but never log the raw config object.
  • Store credentials securely. Use environment variables or a secrets manager.
  • Use HTTPS only. Always use HTTPS URLs for FinTS endpoints.
  • Debug mode. Be cautious with debug: true in production β€” it logs full request/response XML.

Reporting Security Issues

Report security vulnerabilities privately via GitHub's Security tab instead of opening a public issue.

πŸ’‘ Tips and Troubleshooting

Finding Your Bank's FinTS URL

Common Issues

Authentication Errors:

  • Verify username, PIN, and BLZ.
  • Some banks require enabling FinTS/HBCI access in online banking settings.
  • Check if your bank requires product registration.

TAN Requirements:

  • Use tanCallback (FinTS 4.1) or TanRequiredError catch (FinTS 3.0) to handle TAN challenges.

Timeout Issues:

const client = new PinTanClient({ timeout: 60000, maxRetries: 5 });

FinTS 4.1 not working with my bank:

  • Most German retail banks use FinTS 3.0 β€” switch to PinTanClient.
  • Check the bank's FinTS URL: some banks have separate endpoints for v3 and v4.

Mentions

Resources

Contributing

Contributions in the form of well-documented issues or pull requests are welcome.

Contributors

  • Frederick Gnodtke (Original author)
  • Lars Decker (Fork maintainer)

This fork includes several enhancements over the original project:

  • βœ… Updated Dependencies: All dependencies updated to their latest stable versions for better security and performance
  • βœ… Modern TypeScript: Updated to TypeScript 5.x with improved type safety
  • βœ… GitHub Actions: Automated CI/CD pipeline for testing and npm publication
  • βœ… Active Maintenance: Regular updates and dependency maintenance
  • βœ… Published as fints-lib and fints-lib-cli on npm for easier installation

πŸ“¦ Installation

For end users installing the library:

npm install fints-lib
# or
yarn add fints-lib

For the CLI tool:

npm install -g fints-lib-cli
# or
yarn global add fints-lib-cli

Development Setup

This project uses Yarn as the package manager. To set up the development environment:

# Install dependencies
yarn install

# Build all packages
yarn build

# Run tests
yarn test

# Run linting
yarn lint

πŸš€ Quick Start

Minimal Configuration

import { PinTanClient } from "fints-lib";

// Create a client with minimal required configuration
const client = new PinTanClient({
    url: "https://banking.example.com/fints", // Your bank's FinTS URL
    name: "username", // Your banking username
    pin: "12345", // Your banking PIN
    blz: "12345678", // Bank code (BLZ/Bankleitzahl)
});

// List all accounts
const accounts = await client.accounts();
console.log(accounts);

CLI Quick Start

# Install globally
npm install -g fints-lib-cli

# List your accounts
fints-lib list-accounts \
  --url https://banking.example.com/fints \
  -n username \
  -p 12345 \
  -b 12345678

πŸ“– Common Use Cases

1. Check Account Balance

import { PinTanClient } from "fints-lib";

const client = new PinTanClient({
    url: process.env.FINTS_URL,
    name: process.env.FINTS_USERNAME,
    pin: process.env.FINTS_PIN,
    blz: process.env.FINTS_BLZ,
});

// Get all accounts
const accounts = await client.accounts();

// Check balance for first account
const balance = await client.balance(accounts[0]);
console.log(`Account: ${accounts[0].iban}`);
console.log(`Balance: ${balance.value.value} ${balance.value.currency}`);

CLI Example

fints-lib get-balance \
  --url https://banking.example.com/fints \
  -n username -p 12345 -b 12345678 \
  -i DE89370400440532013000

2. Fetch Recent Transactions

import { PinTanClient } from "fints-lib";

const client = new PinTanClient({
    url: process.env.FINTS_URL,
    name: process.env.FINTS_USERNAME,
    pin: process.env.FINTS_PIN,
    blz: process.env.FINTS_BLZ,
});

const accounts = await client.accounts();

// Fetch last 30 days of transactions
const endDate = new Date();
const startDate = new Date(endDate.getTime() - 30 * 24 * 60 * 60 * 1000);

const statements = await client.statements(accounts[0], startDate, endDate);

// Process transactions
statements.forEach((statement) => {
    console.log(`Date: ${statement.date}`);
    statement.transactions.forEach((transaction) => {
        console.log(`  ${transaction.descriptionStructured?.bookingText || "Transaction"}`);
        console.log(`  Amount: ${transaction.amount} ${transaction.currency}`);
        console.log(`  Purpose: ${transaction.purpose || "N/A"}`);
    });
});

CLI Example

# Fetch transactions for a date range
fints-lib fetch-transactions \
  --url https://banking.example.com/fints \
  -n username -p 12345 -b 12345678 \
  -i DE89370400440532013000 \
  -s 2024-01-01 -e 2024-12-31 \
  --json > transactions.json

3. SEPA Credit Transfer (Send Money)

import { PinTanClient, TanRequiredError } from "fints-lib";

const client = new PinTanClient({
    url: process.env.FINTS_URL,
    name: process.env.FINTS_USERNAME,
    pin: process.env.FINTS_PIN,
    blz: process.env.FINTS_BLZ,
});

const accounts = await client.accounts();
const myAccount = accounts[0];

// Prepare transfer
const transfer = {
    debtorName: "John Doe",
    creditor: {
        name: "Recipient Name",
        iban: "DE44500105175407324931",
        bic: "INGDDEFFXXX", // Optional for transfers within SEPA
    },
    amount: 50.0,
    remittanceInformation: "Payment for invoice #12345",
};

try {
    // Initiate transfer
    const result = await client.creditTransfer(myAccount, transfer);
    console.log("Transfer successful:", result.taskId);
} catch (error) {
    if (error instanceof TanRequiredError) {
        // TAN is required - get TAN from user
        const tan = "123456"; // Get from user input or TAN app

        const result = await client.completeCreditTransfer(
            error.dialog,
            error.transactionReference,
            tan,
            error.creditTransferSubmission,
        );
        console.log("Transfer completed:", result.taskId);
    } else {
        throw error;
    }
}

CLI Example

# Transfer money (will prompt for TAN if required)
fints-lib submit-credit-transfer \
  --url https://banking.example.com/fints \
  -n username -p 12345 -b 12345678 \
  --account-iban DE89370400440532013000 \
  --debtor-name "John Doe" \
  --creditor-name "Recipient Name" \
  --creditor-iban DE44500105175407324931 \
  --amount 50.00 \
  --remittance "Payment for invoice #12345"

4. SEPA Direct Debit (Collect Money)

import { PinTanClient, TanRequiredError } from "fints-lib";

const client = new PinTanClient({
    url: process.env.FINTS_URL,
    name: process.env.FINTS_USERNAME,
    pin: process.env.FINTS_PIN,
    blz: process.env.FINTS_BLZ,
});

const accounts = await client.accounts();
const myAccount = accounts[0];

// Prepare direct debit
const debit = {
    creditorName: "My Company GmbH",
    creditorId: "DE98ZZZ09999999999", // Your SEPA creditor ID
    debtor: {
        name: "Customer Name",
        iban: "DE02120300000000202051",
    },
    amount: 99.99,
    mandateId: "MANDATE-2024-001",
    mandateSignatureDate: new Date("2024-01-15"),
    requestedCollectionDate: new Date("2024-12-15"),
    remittanceInformation: "Monthly subscription fee",
};

try {
    const result = await client.directDebit(myAccount, debit);
    console.log("Direct debit submitted:", result.taskId);
} catch (error) {
    if (error instanceof TanRequiredError) {
        const tan = "123456"; // Get from user

        const result = await client.completeDirectDebit(
            error.dialog,
            error.transactionReference,
            tan,
            error.directDebitSubmission,
        );
        console.log("Direct debit completed:", result.taskId);
    } else {
        throw error;
    }
}

CLI Example

fints-lib submit-direct-debit \
  --url https://banking.example.com/fints \
  -n username -p 12345 -b 12345678 \
  --account-iban DE89370400440532013000 \
  --creditor-name "My Company GmbH" \
  --creditor-id DE98ZZZ09999999999 \
  --debtor-name "Customer Name" \
  --debtor-iban DE02120300000000202051 \
  --amount 99.99 \
  --mandate-id MANDATE-2024-001 \
  --mandate-date 2024-01-15 \
  --collection-date 2024-12-15 \
  --remittance "Monthly subscription fee"

5. List Multiple Accounts and Their Balances

import { PinTanClient } from "fints-lib";

const client = new PinTanClient({
    url: process.env.FINTS_URL,
    name: process.env.FINTS_USERNAME,
    pin: process.env.FINTS_PIN,
    blz: process.env.FINTS_BLZ,
});

// Get all accounts with balances
const accounts = await client.accounts();

for (const account of accounts) {
    console.log(`\n${account.accountName || "Account"} (${account.iban})`);
    console.log(`  Type: ${account.accountType || "N/A"}`);

    try {
        const balance = await client.balance(account);
        console.log(`  Balance: ${balance.value.value} ${balance.value.currency}`);
        console.log(`  Available: ${balance.availableBalance?.value || "N/A"}`);
    } catch (error) {
        console.log(`  Balance: Unable to fetch`);
    }
}

Before using any FinTS library you have to register your application with Die Deutsche Kreditwirtschaft in order to get your registration number. Note that this process can take several weeks. First you receive your registration number after a couple days, but then you have to wait anywhere between 0 and 8+ weeks for the registration to reach your bank's server. If you have multiple banks, it probably reaches them at different times.

-- https://github.com/nemiah/phpFinTS

Packages

This library is maintained in a monorepo using lerna. These packages are included:

  • fints-lib - Core library (Take a look for library usage instructions.)
  • fints-lib-cli - Command line interface (Take a look for CLI usage instructions.)

πŸ”’ Security

This library handles sensitive financial data and credentials. Please follow these security best practices:

Credential Handling

  • Never log credentials: The library masks PINs and TANs in debug output, but you should never log the raw configuration object
  • Store credentials securely: Use environment variables or secure credential stores (e.g., AWS Secrets Manager, Azure Key Vault) instead of hardcoding credentials
  • Use HTTPS only: Always use HTTPS URLs for FinTS endpoints to ensure encrypted communication
  • Debug mode: Be cautious when enabling debug mode (debug: true) in production environments, as it logs detailed request/response information

Example: Secure Credential Loading

import { PinTanClient } from "fints-lib";

// βœ… Good: Load from environment variables
const client = new PinTanClient({
    url: process.env.FINTS_URL,
    name: process.env.FINTS_USERNAME,
    pin: process.env.FINTS_PIN,
    blz: process.env.FINTS_BLZ,
    debug: false, // Disable in production
});

// ❌ Bad: Hardcoded credentials
const badClient = new PinTanClient({
    url: "https://example.com/fints",
    name: "username",
    pin: "12345", // Never hardcode!
    blz: "12345678",
});

Reporting Security Issues

If you discover a security vulnerability, please report it privately via GitHub's "Security" tab instead of opening a public issue.

πŸ’‘ Tips and Troubleshooting

Finding Your Bank's FinTS URL

You can find your bank's FinTS endpoint URL in this community database:

Common Issues

Authentication Errors:

  • Verify your username, PIN, and BLZ are correct
  • Some banks require you to enable FinTS/HBCI access in your online banking settings
  • Check if your bank requires registration (see registration note below)

TAN Requirements:

  • Many operations (transfers, direct debits) require TAN authentication
  • Use try-catch with TanRequiredError to handle TAN challenges properly
  • Some banks require TAN even for login - handle with completeLogin()

Timeout Issues:

  • Increase the timeout value in client configuration:
    const client = new PinTanClient({
        // ... other config
        timeout: 60000, // 60 seconds
        maxRetries: 5,
    });

Date Range Queries:

  • Not all banks support all date ranges - some limit how far back you can query
  • Use shorter date ranges if you experience timeouts or errors

Best Practices

  1. Always use environment variables for credentials:

    const client = new PinTanClient({
        url: process.env.FINTS_URL,
        name: process.env.FINTS_USERNAME,
        pin: process.env.FINTS_PIN,
        blz: process.env.FINTS_BLZ,
    });
  2. Enable debug mode during development:

    const client = new PinTanClient({
        // ... credentials
        debug: process.env.NODE_ENV === "development",
    });
  3. Handle errors gracefully:

    import { FinTSError, TanRequiredError, PinError } from "fints-lib";
    
    try {
        const accounts = await client.accounts();
    } catch (error) {
        if (error instanceof TanRequiredError) {
            // Handle TAN requirement
        } else if (error instanceof PinError) {
            // Handle PIN error
        } else if (error instanceof FinTSError) {
            console.error(`FinTS Error [${error.code}]: ${error.message}`);
        }
    }
  4. Close dialogs when done:

    const dialog = await client.startDialog();
    try {
        // ... perform operations
    } finally {
        await dialog.end();
    }

Mentions

FinTS is a complex and old format and this library wouldn't have been possible without the great work of:

  • Prior99/fints - The original repository by Frederick Gnodtke that this fork is based on. Thank you for creating such a comprehensive and well-structured FinTS implementation! πŸ™
  • python-fints was used a reference implementation.
  • Open-Fin-TS-JS-Client provides a demo server used for testing this library.
  • mt940-js is used internally for parsing the MT940 format.

Resources

Contributing

Contributions in the form of well-documented issues or pull-requests are welcome.

Contributors

  • Frederick Gnodtke (Original author)
  • Lars Decker (Fork maintainer)

About

Typescript FinTS client library.

Resources

License

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages

  • TypeScript 99.3%
  • Other 0.7%