Ultra-fast, zero-dependency IBAN & BIC toolkit for Java 8+
🚀 Quick Start • 📖 Examples • 📊 Benchmarks • 📚 Javadoc • 💬 Discussions
iban-commons is a Java IBAN validation library and BIC validator for Java 8+, providing fast and reliable parsing, validation, and formatting of International Bank Account Numbers (IBAN) and Business Identifier Codes (BIC/SWIFT codes).
Designed for high-performance enterprise applications, it covers 120 countries, is Android-compatible (API 21+), and has zero compile or runtime dependencies outside the Java Standard Library.
| Feature | iban-commons | jbanking | Apache Commons | iban4j | garvelink |
|---|---|---|---|---|---|
| Throughput (ops/s) | 7,207,452 | 6,108,009 | 5,243,764 | 1,948,100 | 1,292,532 |
| Memory (B/op) | 48 | 255 | 281 | 1,341 | 1,063 |
| Dependencies | 0 | 0 | 5 | 0 | 0 |
| Java Version | 8+ | 8+ | 8+ | 11+ | 8+ |
| Android (API 21+) | ✅ | ? | ? | ? | ✅ |
| Countries | 120 | 82 | n/a | 111 | 111 |
Maven:
<dependency>
<groupId>de.speedbanking</groupId>
<artifactId>iban-commons</artifactId>
<version>1.8.4</version>
</dependency>Gradle:
implementation 'de.speedbanking:iban-commons:1.8.4'import de.speedbanking.iban.Iban;
// throws InvalidIbanException if invalid
Iban iban = Iban.of("DE91100000000123456789");
// returns Optional<Iban>, does not throw
Optional<Iban> maybeIban = Iban.tryParse("GB82WEST12345698765432");
// validate without parsing
boolean valid = Iban.isValid("FR1420041010050500013M02606");Iban iban = Iban.of("IT60X0542811101000000123456");
iban.getCountryCode(); // "IT"
iban.getCountryName(); // "Italy"
iban.getCountryFlag(); // "🇮🇹"
iban.getBankCode(); // "05428"
iban.getBranchCode(); // "11101"
iban.getAccountNumber(); // "000000123456"
iban.toFormattedString(); // "IT60 X054 2811 1010 0000 0123 456"-
Zero Dependencies & Small Footprint
Keep your build clean and avoid dependency conflicts
-
High Performance
Optimized for throughput and minimal memory footprint. Validates 7,207,452 IBANs per second — 3.7× faster than iban4j, 28× lower memory allocation (48 B/op vs. 1,341 B/op).
-
Simple, intuitive API
Intuitive factory methods (
Iban.of(),Bic.tryParse()), Android-compatible -
Immutability
Iban,Bic,IbanRegistryclasses are immutable and thread-safe -
Java 8 compatibility
Targeting Java 8 for maximum reach, built and verified using JDK 17+ ensuring full binary compatibility
-
Comprehensive Coverage
Full support for IBAN and BIC validation per ISO 13616 and ISO 9362. Covers 120 countries including all from the SWIFT IBAN Registry — more than any comparable Java IBAN validation library.
-
Rich Metadata
Access country names, flags, bank codes, SEPA status, currency information, and regulatory organization details
The API is designed for simplicity, focusing on two main ways to create a valid object: a throwing factory method for quick use and a safe parsing method using Optional.
The Iban class implements Serializable, CharSequence, and Comparable<Iban>.
Use Iban.of() or Iban.parse() when you prefer an exception for validation failures.
import de.speedbanking.iban.Iban;
import de.speedbanking.iban.InvalidIbanException;
String ibanInput = "DE91100000000123456789";
try {
Iban iban = Iban.of(ibanInput);
// getters
System.out.println("Country Code: " + iban.getCountryCode()); // DE
System.out.println("Check Digits: " + iban.getCheckDigits()); // 91
System.out.println("BBAN : " + iban.getBban()); // 100000000123456789
System.out.println("Bank Code : " + iban.getBankCode()); // 10000000
System.out.println("Account No : " + iban.getAccountNumber()); // 0123456789
// output
System.out.println("Normalized : " + iban.toString()); // DE91100000000123456789
System.out.println("Formatted : " + iban.toFormattedString()); // DE91 1000 0000 0123 4567 89
} catch (InvalidIbanException ex) {
System.out.println("IBAN validation failed: " + ex.getMessage());
}Use Iban.tryParse() when dealing with external or uncertain input and to avoid exceptions for control flow.
import de.speedbanking.iban.Iban;
import java.util.Optional;
Optional<Iban> optionalIban = Iban.tryParse("PS92PALS000000000400123456702");
optionalIban.ifPresent(iban -> {
println("Country Code: " + iban.getCountryCode()); // PS
println("Country Name: " + iban.getCountryName()); // Palestine
println("Country Flag: " + iban.getCountryFlag()); // 🇵🇸
println("Organisation: " + iban.getOrganisation()); // Palestine Monetary Authority
});Batch Processing:
List<String> ibanStrings = loadFromDatabase();
List<Iban> validIbans = ibanStrings.stream()
.map(Iban::tryParse)
.flatMap(Optional::stream)
.collect(Collectors.toList());SEPA Filtering:
Iban iban = Iban.of("CH9300762011623852957");
if (iban.isSepa()) {
processSepaPayment(iban);
} else {
processCrossBorderPayment(iban);
}Use RandomIban to generate syntactically correct, structurally valid IBANs with proper ISO 7064 Mod 97-10 check digits — ideal for testing, seeding demo data, or populating sandboxes.
Any supported country (non-deterministic):
import de.speedbanking.iban.RandomIban;
Iban rdIban = RandomIban.of(); // random country
Iban deIban = RandomIban.of("DE"); // specific country
Iban sepaIban = RandomIban.ofSepa(); // random SEPA countryReproducible generation with a seeded Random:
Pass a seeded java.util.Random to get deterministic output — the same seed always produces the same IBAN for the same country, which is useful for unit tests and snapshot tests.
import java.util.Random;
// Always produces the same IBAN for seed 42 + country "IT"
Iban itIban = RandomIban.builder()
.country("IT")
.random(new Random(42L))
.build();
// Reproducible SEPA IBAN
Iban sepaIban = RandomIban.builder()
.sepaOnly() // random SEPA country
.random(new Random(42L))
.build();Note: Generated IBANs are syntactically and structurally valid but do not correspond to real bank accounts. Do not use them for actual financial transactions.
The Bic class implements Serializable, CharSequence, and Comparable<Bic>.
A BIC comparison is always based on the 11-character representation (toBic11()), meaning BIC-8 and its BIC-11 equivalent are considered equal.
import de.speedbanking.bic.Bic;
// BIC-11
Bic bic11 = Bic.of("PALSPS22XXX"); // Bank of Palestine P.S.C.
System.out.println("Bank Code : " + bic11.getBankCode()); // PALS
System.out.println("Country Code : " + bic11.getCountryCode()); // PS
System.out.println("Location Code: " + bic11.getLocationCode()); // 22
System.out.println("Branch Code : " + bic11.getBranchCode()); // XXX
System.out.println("is BIC-11 : " + bic11.isBic11()); // true
System.out.println("is BIC-8 : " + bic11.isBic8()); // false
System.out.println("to BIC-8 : " + bic11.toBic8()); // PALSPS22
// BIC-8
Bic bic8 = Bic.of("MARKDEFF"); // Deutsche Bundesbank, Zentrale
System.out.println("is BIC-8 : " + bic8.isBic8()); // true
System.out.println("is BIC-11 : " + bic8.isBic11()); // false
System.out.println("to BIC-11 : " + bic8.toBic11()); // MARKDEFFXXXimport de.speedbanking.bic.Bic;
Bic.tryParse("INVALIDBIC").ifPresentOrElse(
bic -> System.out.println("Valid BIC: " + bic),
() -> System.err.println("Invalid BIC")
);// Before (iban4j)
import org.iban4j.CountryCode;
import org.iban4j.Iban;
import org.iban4j.IbanFormatException;
try {
Iban iban = new Iban.Builder()
.countryCode(CountryCode.DE)
.bankCode("37040044")
.accountNumber("0532013000")
.build();
} catch (IbanFormatException ex) { }
// After (iban-commons)
import de.speedbanking.iban.Iban;
Iban iban = Iban.of("DE89370400440532013000");// Before (Apache Commons Validator)
import org.apache.commons.validator.routines.IBANValidator;
IBANValidator validator = IBANValidator.getInstance();
boolean valid = validator.isValid("GB82WEST12345698765432");
// After (iban-commons)
import de.speedbanking.iban.Iban;
boolean valid = Iban.isValid("GB82WEST12345698765432");
// or parse for component access
Iban iban = Iban.of("GB82WEST12345698765432");iban-commons is designed for high throughput and minimal overhead.
We use the Java Microbenchmark Harness (JMH) to compare the throughput of iban-commons against popular Java IBAN libraries: iban4j, Apache Commons Validator, garvelink java-iban, and jbanking.
Measured on Intel Core i7-1165G7 @ 2.80GHz, OpenJDK 21.0.7, Linux, single core (taskset -c 0),
Generational ZGC, -XX:-StackTraceInThrowable.
30 measurement iterations (3 forks × 10 iterations × 2 s each).
| Library | Throughput (ops/s) | Memory (B/op) | vs. iban-commons |
|---|---|---|---|
| 🌟 iban-commons | 7,207,452 | 48 | baseline |
| jbanking | 6,108,009 | 255 | 1.2× slower |
| Apache Commons | 5,243,764 | 281 | 1.4× slower |
| iban4j | 1,948,100 | 1,341 | 3.7× slower |
| garvelink java-iban | 1,292,532 | 1,063 | 5.6× slower |
| Library | Throughput (ops/s) | Memory (B/op) | vs. iban-commons |
|---|---|---|---|
| 🌟 iban-commons | 11,986,711 | 40 | baseline |
| jbanking | 10,080,195 | 153 | 1.2× slower |
| Apache Commons | 9,853,673 | 171 | 1.2× slower |
| iban4j | 1,992,301 | 1,099 | 6.0× slower |
| garvelink java-iban | 1,508,470 | 814 | 7.9× slower |
Each invalid IBAN is derived from a valid one by applying one of six sabotage strategies with equal probability: incrementing a check digit (triggering a Mod-97 failure), replacing the country code with the non-registered code XY, substituting a valid but mismatched ISO 3166 country code, injecting a letter into the numeric BBAN section, swapping two adjacent characters (transposition), or truncating the string below the minimum structural length.
- Leading Throughput:
iban-commonsis the fastest across both valid and invalid input — reaching 7,207,452 ops/s on the accept path and 11,986,711 ops/s on early rejection. - Fast Rejection: Invalid IBANs are rejected faster than valid ones because many fail length or country-code checks before the full Mod-97 computation is reached.
- Minimal GC Pressure: Memory allocation is 6×–28× lower than competing libraries, thanks to an ASCII-math Mod-97 approach that avoids intermediate
StringandBigIntegerallocations.
Note on
-XX:-StackTraceInThrowable: All forks run with this JVM flag, which suppresses stack trace generation to isolate pure algorithmic cost. This makes the comparison fair for libraries using exceptions for control flow (notablyiban4j). For production-realistic measurements, remove the flag and re-run.
All performance tests are fully open and available in the SpeedBankingDe/iban-commons-benchmarks repository.
Which countries are supported?
120 countries including all from the SWIFT IBAN Registry Release 100 (October 2025):
- All SEPA countries including major economies: Germany, UK, Switzerland, Norway, etc.
- All known non-SEPA countries that support IBAN
- Full list available in the source code
Is this library production-ready?
Yes, and already in use in production systems. Features:
- Comprehensive test suite (>99% coverage)
- Zero runtime dependencies
- Immutable, thread-safe design
- Regular updates with SWIFT registry
How do I validate IBANs from user input?
String userInput = " de91 1000 0000 0123 4567 89 ";
// Option 1: Safe parsing (recommended for user input)
Optional<Iban> maybeIban = Iban.tryParse(userInput);
maybeIban.ifPresent(iban -> processPayment(iban));
// Option 2: Quick validation
if (Iban.isValid(userInput)) {
Iban iban = Iban.of(userInput);
}Can I use this with Spring Boot?
Yes! Works seamlessly with Spring Boot. For Jakarta Bean Validation integration.
Example custom validator:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = IbanValidator.class)
public @interface ValidIban {
String message() default "Invalid IBAN";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}Where can I find the full API documentation?
The complete Javadoc is hosted on javadoc.io.
It covers all public classes and methods including Iban, Bic, IbanRegistry, and the validation API.
Can I use this library on Android?
Yes. iban-commons is Android-compatible from API level 21 (Android 5.0) onwards.
The primary API (Iban.tryParse(), Bic.tryParse()) uses java.util.Optional, which requires API 24+.
For projects targeting API 21–23, use the Android-safe alternatives introduced in v1.8.3:
// Android-safe (API 21+) — returns null instead of Optional
Iban iban = Iban.tryParseOrNull("DE91100000000123456789");
Bic bic = Bic.tryParseOrNull("MARKDEFF");The library has zero compile or runtime dependencies and does not use java.time types in its public API.
Is the library thread-safe?
Yes. Both Iban and Bic are immutable value objects — all fields are final and set in the constructor.
They can be safely shared across threads without synchronization.
The validation and registry classes are stateless and equally safe for concurrent use.
What's the difference between IBAN validation and checksum validation?
iban-commons performs complete validation:
- Format validation: Country code, length, character set
- Structural validation: Country-specific BBAN pattern
- Checksum validation: MOD-97 algorithm (ISO 13616)
- Country-specific rules: When available via
CountryValidator
Simple checksum validation alone is insufficient for real-world use.
We welcome contributions! See CONTRIBUTING.md for guidelines.
Quick Ways to Contribute:
- Report bugs via Issues
- Suggest features in Discussions
- Improve documentation
- Submit pull requests
Development Setup:
- JDK 17 or higher required to build the project
- Maven 3.9+ required
git clone https://github.com/SpeedBankingDe/iban-commons.git
cd iban-commons
mvn clean verify- 💡 Questions? Ask in Discussions
- 🐛 Found a bug? Open an Issue
- 📚 API Reference: Javadoc on javadoc.io
- 🌐 Website: www.speedbanking.de
** Please star this repo if you find it useful! **
To report a vulnerability, please use GitHub's private Security Advisory mechanism rather than opening a public issue.
For details on the project's security policy and the serialization hardening built
into Iban and Bic, see SECURITY.md.
This project is licensed under the Apache License, Version 2.0. You can find the full text of the license here.
|
Enjoying
iban-commons?
Please leave a 🌟 to support the project! Your stars help to keep open source development visible and going. |