minted_finance
IBANs, BICs, ISINs and payment card numbers as well-modelled value types.
Part of the minted family: pure-Dart value types built on
parse, don't validate, so the parser is the only door in and anything that came through it is
well-formed by construction. Once you hold an Iban, it is a checksum-valid IBAN.
Install
dart pub add minted_finance
minted comes with it, holding the vocabulary a parse hands back
(ParseOutcome, MintedFailure), and so does
minted_constraints for the primitives this package's
getters return. Nothing here drags in
another domain's engine.
What's in the box
| Type | What it guarantees | Standard |
|---|---|---|
Iban |
structure, country length, and the mod-97 checksum | ISO 13616 |
Bic |
a SWIFT code: structure and a real country, folded to 11 | ISO 9362 |
PaymentCardNumber |
digits, the 8-to-19 window, and Luhn. Masked when printed | ISO/IEC 7812 |
Isin |
a securities ID: charset, 2-letter prefix, and Luhn over its letter expansion | ISO 6166 |
The check digits actually run: Iban computes mod-97 rather than matching a country's shape, and
PaymentCardNumber is a class rather than an extension type precisely so printing one can't leak
a PAN.
A quick taste
final iban = Iban.tryParse('gb29 nwbk 6016 1331 9268 19')!;
iban.value; // 'GB29NWBK60161331926819' (compact)
iban.countryCode; // 'GB'
iban.checkDigits; // (first: Digit, second: Digit)
iban.formatted; // 'GB29 NWBK 6016 1331 9268 19' (grouped paper form)
// Bic: the 8- and 11-character spellings of one office are the same value, XXX being the office:
final bic = Bic.tryParse('deut de ff')!;
bic.value; // 'DEUTDEFFXXX'
bic.bic8; // 'DEUTDEFF' (the short form, rebuilt)
bic == Bic.tryParse('DEUTDEFFXXX'); // true
Bic.tryParse('DEUTZZFF'); // null: ZZ is not a country
// PaymentCardNumber masks itself, so a stray log line can't leak the number:
final card = PaymentCardNumber.tryParse('4242 4242 4242 4242')!;
'$card'; // 'PaymentCardNumber(••••4242)'
card.value; // '4242424242424242' (the only member that hands the number back)
card.cardScheme; // CardScheme.visa (read off the prefix, never validated)
// the scheme also reads from partial input, so a form can show the brand while you type:
PaymentCardNumber.cardSchemesOf('4'); // {CardScheme.visa}
PaymentCardNumber.tryParse('4242424242424243'); // null: fails the Luhn check
// Isin runs Luhn over the number with each letter expanded to two digits:
final isin = Isin.tryParse('au0000xvgza3')!;
isin.value; // 'AU0000XVGZA3'
isin.nsin; // '0000XVGZA'
Isin.tryParse('US0378331006'); // null: fails the Luhn check
The runnable version is the example.
Named constants
A type's named values live in a companion <Type>Constants namespace, so the type itself stays its
parsing API. These are the values documentation reaches for, as
const, so they go where a tryParse(...)! can't.
IbanConstants.example.formatted; // 'GB82 WEST 1234 5698 7654 32'
BicConstants.exampleDe.bic8.value; // 'BANKDEFF'
IsinConstants.example.value; // 'US0000000002'
PaymentCardNumberConstants.testVisa.cardScheme; // CardScheme.visa
Habits, not standards. Nothing here is reserved for documentation.
WESTandBANKname no real institution, and the PANs are the ones every processor publishes for testing.
One shape, every type
Type.tryParse(input)hands back the value, ornullwhen the input isn't validType.parse(input)hands back aParseOutcome: the value, or a typed failure (IbanFailure,BicFailure,IsinFailure,PaymentCardNumberFailure) you canswitchon, or read as a form-field message via.reasonOrNull. No door throws- value equality, a canonical
.valuenormalised on parse, andfromComponentsfor parts you already hold
The minted README is the family guide: the package index,
handling failures, and the one caveat (never cast into a minted type).
APPENDIX.md carries the design rationale for these types: the alternatives that were weighed and dropped, and what each decision costs.
Libraries
- minted_finance
- IBANs, BICs, ISINs and payment card numbers as well-modelled value types.