diff --git a/README.md b/README.md index c93064e..f8d9924 100644 --- a/README.md +++ b/README.md @@ -55,35 +55,268 @@ yarn lint ## 🚀 Quick Start +### Minimal Configuration + ```typescript import { PinTanClient } from "fints-lib"; +// Create a client with minimal required configuration const client = new PinTanClient({ - url: "https://example.com/fints", - name: "username", - pin: "12345", - blz: "12345678", + 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 accounts +// List all accounts const accounts = await client.accounts(); console.log(accounts); +``` + +### CLI Quick Start + +```bash +# 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 + +```typescript +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** +```bash +fints-lib get-balance \ + --url https://banking.example.com/fints \ + -n username -p 12345 -b 12345678 \ + -i DE89370400440532013000 +``` + +### 2. Fetch Recent Transactions + +```typescript +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); -// Fetch statements -const startDate = new Date("2024-01-01"); -const endDate = new Date("2024-12-31"); const statements = await client.statements(accounts[0], startDate, endDate); -console.log(statements); + +// 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'}`); + }); +}); ``` -For CLI usage: +**CLI Example** +```bash +# 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) + +```typescript +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** +```bash +# 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) + +```typescript +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** ```bash -# List accounts -fints-lib list-accounts --url https://example.com/fints -n username -p 12345 -b 12345678 +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" +``` -# Fetch transactions -fints-lib fetch-transactions --url https://example.com/fints -n username -p 12345 -b 12345678 -i DE111234567800000001 -s 2024-01-01 -e 2024-12-31 +### 5. List Multiple Accounts and Their Balances + +```typescript +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. @@ -135,6 +368,86 @@ const badClient = new PinTanClient({ 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: +- [FinTS Institute Database](https://github.com/jhermsmeier/fints-institute-db) + +### 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: + ```typescript + 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:** + ```typescript + 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:** + ```typescript + const client = new PinTanClient({ + // ... credentials + debug: process.env.NODE_ENV === 'development', + }); + ``` + +3. **Handle errors gracefully:** + ```typescript + 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:** + ```typescript + 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: diff --git a/packages/fints-cli/README.md b/packages/fints-cli/README.md index f2efe65..a91d86c 100644 --- a/packages/fints-cli/README.md +++ b/packages/fints-cli/README.md @@ -215,6 +215,122 @@ fints-lib submit-direct-debit --url https://example.com/fints --name username -- --mandate-date 2022-01-10 --collection-date 2022-01-15 --remittance "Invoice 0815" ``` +## Practical CLI Examples + +### Using Environment Variables + +To avoid typing credentials repeatedly, create a shell script or use environment variables: + +```bash +#!/bin/bash +# save as fints-env.sh + +export FINTS_URL="https://banking.example.com/fints" +export FINTS_USER="myusername" +export FINTS_PIN="mypin" +export FINTS_BLZ="12345678" + +# Then use in commands: +# source fints-env.sh +# fints-lib list-accounts --url $FINTS_URL -n $FINTS_USER -p $FINTS_PIN -b $FINTS_BLZ +``` + +### Quick Account Overview + +```bash +# Get account list in JSON format for processing +fints-lib list-accounts \ + --url https://banking.example.com/fints \ + -n username -p 12345 -b 12345678 \ + --json | jq '.' + +# Check balance for a specific account +fints-lib get-balance \ + --url https://banking.example.com/fints \ + -n username -p 12345 -b 12345678 \ + -i DE89370400440532013000 \ + --json | jq '.value' +``` + +### Export Transactions to File + +```bash +# Fetch and save transactions as JSON +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-2024.json + +# Or save verbose output to text file +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 \ + --verbose > transactions-2024.txt +``` + +### Monthly Statement Automation + +```bash +#!/bin/bash +# monthly-statement.sh - Fetch last month's transactions + +# Calculate date range for last month +# For Linux (GNU date): +START_DATE=$(date -d "last month" +%Y-%m-01) +END_DATE=$(date -d "-1 day $(date +%Y-%m-01)" +%Y-%m-%d) + +# For macOS (BSD date), install GNU coreutils and use 'gdate': +# brew install coreutils +# START_DATE=$(gdate -d "last month" +%Y-%m-01) +# END_DATE=$(gdate -d "-1 day $(gdate +%Y-%m-01)" +%Y-%m-%d) + +# Fetch transactions +fints-lib fetch-transactions \ + --url "$FINTS_URL" \ + -n "$FINTS_USER" \ + -p "$FINTS_PIN" \ + -b "$FINTS_BLZ" \ + -i "$ACCOUNT_IBAN" \ + -s "$START_DATE" \ + -e "$END_DATE" \ + --json > "statement-$(date +%Y-%m).json" + +echo "Statement saved for period: $START_DATE to $END_DATE" +``` + +### Check Multiple Accounts + +```bash +#!/bin/bash +# check-all-accounts.sh + +ACCOUNTS=("DE89370400440532013000" "DE89370400440532013001") + +for IBAN in "${ACCOUNTS[@]}"; do + echo "Checking account: $IBAN" + fints-lib get-balance \ + --url "$FINTS_URL" \ + -n "$FINTS_USER" \ + -p "$FINTS_PIN" \ + -b "$FINTS_BLZ" \ + -i "$IBAN" \ + --json | jq -r '" Balance: \(.value.value) \(.value.currency)"' + echo "" +done +``` + +## Tips + +- Use `--json` flag for machine-readable output that can be processed with tools like `jq` +- Use `--verbose` flag to see detailed information during development +- Use `--debug` flag to troubleshoot connection issues +- Store credentials in environment variables or a secure credential manager +- Use the [FinTS Institute Database](https://github.com/jhermsmeier/fints-institute-db) to find your bank's URL + ## Resources - [Database of banks with their URLs](https://github.com/jhermsmeier/fints-institute-db) diff --git a/packages/fints/README.md b/packages/fints/README.md index 2e4bb17..d9d7021 100644 --- a/packages/fints/README.md +++ b/packages/fints/README.md @@ -15,26 +15,56 @@ npm install fints-lib yarn add fints-lib ``` -## Example +## Quick Start Examples + +### Basic Account Information ```typescript import { PinTanClient } from "fints-lib"; -const startDate = new Date("2018-08-10T12:00:00Z"); -const endDate = new Date("2018-10-10T12:00:00Z"); +// Initialize client with minimal configuration +const client = new PinTanClient({ + url: "https://banking.example.com/fints", + name: "username", + pin: "12345", + blz: "12345678", +}); + +// Fetch all accounts +const accounts = await client.accounts(); +console.log(accounts); // List of all accounts + +// Get balance for first account +const balance = await client.balance(accounts[0]); +console.log(`Balance: ${balance.value.value} ${balance.value.currency}`); +``` + +### Fetching Transactions + +```typescript +import { PinTanClient } from "fints-lib"; const client = new PinTanClient({ - url: "https://example.com/fints", + url: "https://banking.example.com/fints", name: "username", - pin: 12345, - blz: 12345678, + pin: "12345", + blz: "12345678", }); const accounts = await client.accounts(); -console.info(accounts); // List of all accounts. +// Fetch transactions for a date range +const startDate = new Date("2024-01-01"); +const endDate = new Date("2024-12-31"); const statements = await client.statements(accounts[0], startDate, endDate); -console.info(statements); // List of all statements with transactions in specified date range. + +// Process transactions +statements.forEach(statement => { + console.log(`Statement from ${statement.date}`); + statement.transactions.forEach(tx => { + console.log(` ${tx.descriptionStructured?.bookingText}: ${tx.amount} ${tx.currency}`); + }); +}); ``` @@ -164,6 +194,119 @@ const formatted = formatErrorCode("9942", "Custom message"); // Output: "[9942] PIN falsch - Custom message" ``` +## Common Use Cases + +### Check Account Balance + +```typescript +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(); + +// Get balance for a specific account +const balance = await client.balance(accounts[0]); +console.log(`Current Balance: ${balance.value.value} ${balance.value.currency}`); +console.log(`Available: ${balance.availableBalance?.value || 'N/A'}`); +``` + +### Fetch Recent Transactions + +```typescript +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(); + +// Get transactions from the last 30 days +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 => { + console.log(`\nStatement: ${statement.date}`); + statement.transactions.forEach(tx => { + const booking = tx.descriptionStructured?.bookingText || 'Transaction'; + const purpose = tx.purpose || ''; + console.log(` ${booking}: ${tx.amount} ${tx.currency}`); + if (purpose) console.log(` Purpose: ${purpose}`); + }); +}); +``` + +### List All Accounts with Details + +```typescript +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(); + +console.log(`Found ${accounts.length} account(s):\n`); + +for (const account of accounts) { + console.log(`Account: ${account.accountName || 'Unnamed'}`); + console.log(` IBAN: ${account.iban}`); + console.log(` Type: ${account.accountType || 'N/A'}`); + console.log(` Number: ${account.accountNumber || 'N/A'}`); + + try { + const balance = await client.balance(account); + console.log(` Balance: ${balance.value.value} ${balance.value.currency}`); + } catch (error) { + console.log(` Balance: Unable to retrieve`); + } + console.log(); +} +``` + +### Working with Environment Variables (Recommended) + +```typescript +import { PinTanClient } from "fints-lib"; + +// Create client using environment variables for credentials +// Ensure environment variables are set before running +if (!process.env.FINTS_URL || !process.env.FINTS_USERNAME || + !process.env.FINTS_PIN || !process.env.FINTS_BLZ) { + throw new Error("Required environment variables are not set"); +} + +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: process.env.NODE_ENV === "development", +}); + +// Example .env file: +// FINTS_URL=https://banking.example.com/fints +// FINTS_USERNAME=myusername +// FINTS_PIN=mypin +// FINTS_BLZ=12345678 +// NODE_ENV=development +``` + ## Missing