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.

🎯 Improvements in this Fork

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.00,
    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

No packages published

Languages

  • TypeScript 98.4%
  • JavaScript 1.4%
  • Makefile 0.2%