tomaj / bank-mails-parser
Professional PHP library for parsing bank confirmation emails from Slovak and Czech banks
Requires
- php: ^8.4
- ext-mbstring: *
- singpolyma/openpgp-php: ^0.7
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.0
- infection/infection: ^0.35
- phpstan/phpstan: ^2.1
- phpstan/phpstan-strict-rules: ^2.0
- phpunit/phpunit: ^13.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
- dev-master
- 5.0.0
- 4.1.0
- 4.0.0
- 3.0.0
- 2.8.0
- 2.7.0
- 2.6.0
- 2.5.0
- 2.4.0
- 2.3.0
- 2.2.0
- 2.1.0
- 2.0.0
- 1.1.0
- 1.0.0
- dev-docs/slovak-translation
- dev-docs/bestpractices-badge
- dev-docs/security-policy
- dev-docs/new-logo
- dev-docs/readme-docs-link
- dev-fix/docs-logo-links
- dev-cursor/state-of-the-art-improvements-3d2b
- dev-docs-archive/4.x
- dev-feature/improve-test-coverage
- dev-feature/vub-parser
- dev-feature/phpunit-upgrade
- dev-feature/coverage-reporting
- dev-cursor/upgrade-package-to-php-8-with-modern-ci-bfe5
This package is auto-updated.
Last update: 2026-10-12 16:09:15 UTC
README
Professional PHP library for parsing bank confirmation emails from Slovak and Czech banks. Extract transaction details, account numbers, amounts, and banking symbols automatically from email notifications.
📖 Documentation: https://tomaj.github.io/bank-mails-parser/
Getting started · API reference · Upgrade guide 4.x → 5.0 · 4.x docs · Slovenčina
Why This Library?
Manual processing of bank notification emails is error-prone and time-consuming. This library provides a robust, tested solution for automatically extracting payment information from bank emails, enabling seamless integration with accounting systems, payment verification workflows, and financial automation.
Features
- Multiple Bank Support: TatraBanka, ČSOB (CZ/SK), VÚB
- Multiple Email Formats: Decoded plain text and PGP encrypted statements. HTML must be converted to text before parsing.
- Comprehensive Data Extraction: Amounts, currencies, account numbers, banking symbols (VS, KS, SS)
- Multi-Transaction Support: Process emails containing multiple payments
- PGP Decryption: Handle encrypted bank statements
- Type Safety: Full PHP 8.4+ strict typing with final readonly classes
- Well Tested: High code coverage, comprehensive test suite, mutation tested
- Production Ready: PHPStan level max, PER-CS + PHP 8.4 migration rules compliant
Supported Banks
| Bank | Country | Parser Classes | Email Types |
|---|---|---|---|
| TatraBanka | Slovakia | TatraBankaMailParserTatraBankaSimpleMailParserTatraBankaStatementMailParser |
Plain text ComfortPay PGP encrypted |
| ČSOB | Czech Republic | CsobMailParser |
Decoded text, possibly several transactions |
| ČSOB | Slovakia | SkCsobMailParser |
Decoded text, possibly several transactions |
| VÚB | Slovakia | VubMailParser |
Plain text |
Requirements
- PHP 8.4 or 8.5
ext-mbstring- Composer
Documentation
Installation
composer require tomaj/bank-mails-parser ^5.0
Upgrading?
- From 4.x to 5.0: See the 5.0 Upgrade Guide for breaking changes and migration steps
- From 3.x to 4.0: See the 4.0 Upgrade Guide
Quick Start
<?php use Tomaj\BankMailsParser\BankMailsParser; $parser = new BankMailsParser(); $mailContent = $parser->parse($emailBody, $sender, $subject); echo $mailContent->money?->toDecimalString() . ' ' . $mailContent->money?->currency->value . "\n"; echo $mailContent->vs . "\n"; echo $mailContent->accountNumber . "\n"; // ČSOB mail and statement files can contain more than one transaction. foreach ($parser->parseAll($emailBody, $sender, $subject) as $row) { echo $row->money?->toDecimalString(), "\n"; }
Pass $sender (the From address). parse() throws UnsupportedBankException, InvalidEmailFormatException, AmbiguousBankException, or MultipleTransactionsException when the mail has more than one transaction. tryParse() returns null for those. Use parseAll() for ČSOB and for statements.
Usage Examples
TatraBanka
Standard Notification Emails
use Tomaj\BankMailsParser\Parser\TatraBanka\TatraBankaMailParser; $parser = new TatraBankaMailParser(); $mailContent = $parser->parse($emailBody); $mailContent->money?->amount; // int minor units, or null money $mailContent->money?->toDecimalString(); // "12.31" $mailContent->money?->currency->value; // "EUR" $mailContent->accountNumber; // string|null $mailContent->sourceAccountNumber; // string|null $mailContent->vs; // string|null (Variable Symbol) $mailContent->ks; // string|null (Constant Symbol) $mailContent->ss; // string|null (Specific Symbol) $mailContent->transactionDate; // ?DateTimeImmutable $mailContent->receiverMessage; // string|null $mailContent->description; // string|null
ComfortPay Payments
use Tomaj\BankMailsParser\Parser\TatraBanka\TatraBankaSimpleMailParser; $parser = new TatraBankaSimpleMailParser(); $mailContent = $parser->parse($emailBody); // Additional ComfortPay fields $mailContent->cid; // string|null (Client ID) $mailContent->sign; // string|null (HMAC or SIGN) $mailContent->res; // string|null (Result code: OK, FAIL) $mailContent->tres; // string|null (also copied into res when present) $mailContent->ac; // string|null $mailContent->cc; // string|null (masked card) $mailContent->tid; // string|null $mailContent->txn; // string|null (Transaction ID) $mailContent->rc; // string|null (Return code)
PGP Encrypted Statements
use Tomaj\BankMailsParser\Parser\TatraBanka\TatraBankaStatementMailParser; use Tomaj\BankMailsParser\Parser\TatraBanka\TatraBankaMailDecryptor; $decryptor = new TatraBankaMailDecryptor( '/path/to/private-key.asc', 'your-passphrase' ); $parser = new TatraBankaStatementMailParser($decryptor); $mailContents = $parser->parseMulti($encryptedEmailBody); foreach ($mailContents as $mailContent) { echo $mailContent->money?->toDecimalString() . ' ' . $mailContent->money?->currency->value . "\n"; }
ČSOB Czech Republic
use Tomaj\BankMailsParser\Parser\Csob\CsobMailParser; $parser = new CsobMailParser(); $mailContents = $parser->parseMulti($emailBody); foreach ($mailContents as $mailContent) { echo "VS: " . $mailContent->vs . "\n"; echo 'Amount: ' . $mailContent->money?->toDecimalString() . ' ' . $mailContent->money?->currency->value . "\n"; echo "Account: " . $mailContent->accountNumber . "\n"; echo "From: " . $mailContent->sourceAccountNumber . "\n"; }
ČSOB Slovakia
use Tomaj\BankMailsParser\Parser\Csob\SkCsobMailParser; $parser = new SkCsobMailParser(); $mailContents = $parser->parseMulti($emailBody); foreach ($mailContents as $mailContent) { echo "VS: " . $mailContent->vs . "\n"; echo "KS: " . $mailContent->ks . "\n"; echo 'Amount: ' . $mailContent->money?->toDecimalString() . ' ' . $mailContent->money?->currency->value . "\n"; }
VÚB
use Tomaj\BankMailsParser\Parser\Vub\VubMailParser; $parser = new VubMailParser(); $mailContent = $parser->parse($emailBody); echo $mailContent->vs . "\n"; echo $mailContent->ks . "\n"; echo $mailContent->money?->toDecimalString() . "\n"; echo $mailContent->transactionDate?->format('Y-m-d') . "\n";
MailContent API
The MailContent object is an immutable value object (PHP 8.4+ final readonly class) with public readonly properties:
// Financial Information $mailContent->money: ?Money // amount (int minor units) + Currency $mailContent->money?->toDecimalString(); // decimal string, e.g. "12.34" $mailContent->transactionDate: ?DateTimeImmutable // Account Information $mailContent->accountNumber: ?string // Destination account $mailContent->sourceAccountNumber: ?string // Source account (when available) // Banking Symbols $mailContent->vs: ?string // Variable Symbol $mailContent->ks: ?string // Constant Symbol $mailContent->ss: ?string // Specific Symbol // Transaction Details $mailContent->receiverMessage: ?string // Payment message $mailContent->description: ?string // Transaction description $mailContent->txn: ?string // Transaction ID // TatraBanka ComfortPay Specific $mailContent->cid: ?string // Client ID $mailContent->sign: ?string // HMAC signature $mailContent->res: ?string // Result code (OK, FAIL) $mailContent->ac: ?string // Authorization code $mailContent->cc: ?string // Credit card info $mailContent->rc: ?string // Return code
All properties return null if the data is not present in the email.
Integration with IMAP
Example integration with tomaj/imap-email-downloader:
use Tomaj\ImapMailDownloader\Downloader; use Tomaj\ImapMailDownloader\MailCriteria; use Tomaj\ImapMailDownloader\Email; use Tomaj\BankMailsParser\BankMailsParser; $downloader = new Downloader('imap.example.com', 993, 'user@example.com', 'password'); $criteria = new MailCriteria(); $criteria->setFrom('notifications@tatrabanka.sk'); $downloader->fetch($criteria, function(Email $email) { $parser = new BankMailsParser(); try { foreach ($parser->parseAll($email->getBody(), 'notifications@tatrabanka.sk') as $mailContent) { processPayment($mailContent); } } catch (\Tomaj\BankMailsParser\Exception\ParseException) { // unrecognized mail, or payer text that looks like a second transaction } return true; });
Error Handling
parse() throws. tryParse() returns null. Both methods exist on BankMailsParser and on each parser.
use Tomaj\BankMailsParser\BankMailsParser; use Tomaj\BankMailsParser\Exception\InvalidEmailFormatException; use Tomaj\BankMailsParser\Exception\UnsupportedBankException; $parser = new BankMailsParser(); try { processTransaction($parser->parse($emailBody, $sender, $subject)); } catch (UnsupportedBankException $exception) { log($exception->getMessage()); } catch (InvalidEmailFormatException $exception) { log($exception->getMessage()); }
Security Considerations
- Store PGP private keys outside the web root
- Use environment variables for sensitive configuration
- Validate all extracted amounts before processing payments
- Never expose raw email content in error messages
- Log parsing failures for security monitoring
- This library does not verify CardPay
HMACorSIGN, and it does not verify DKIM. Check both before you book a payment. A passed sender limits parsing to that bank's parsers, and a mail claimed by two parsers throwsAmbiguousBankException. Free-text fields cannot supply the amount, currency, account, or date. The variable symbol can come from a message the payer wrote (Informacia pre prijemcu,Zpráva příjemci,Identifikace,Účel platby, a mandate reference, or a creditor reference). WhenReferencia platitelais absent, Tatra still takes the last number in that message, as in 4.1.0. When you pair a payment, always check the amount as well, and another identifier when you have one. A ČSOB mail with several transactions and no balance line is parsed the same way as 4.1.0. That is a known limitation: without those lines a forged transaction in payer text cannot be distinguished from a real one. Check the amount against the bank API or a statement. DKIM alone does not make a parsed amount safe.
Contributing
We welcome contributions! Please see CONTRIBUTING.md for:
- How to add support for new banks
- Development setup and coding standards
- Testing requirements and quality checks
- Pull request process
License
This library is licensed under the MIT License.
Support
- Documentation: English · Slovenčina
- Getting started: https://tomaj.github.io/bank-mails-parser/guide/getting-started
- API reference: https://tomaj.github.io/bank-mails-parser/api/reference
- Upgrade guide 4.x → 5.0: UPGRADE-5.0.md
- 4.x docs: English · Slovenčina
- Coverage Report: https://tomaj.github.io/bank-mails-parser/coverage/
- Issues: GitHub Issues
- Email: tomasmajer@gmail.com
- Discussions: GitHub Discussions
Changelog
See CHANGELOG.md for a detailed history of changes.