# Groups with the baseline only

Many standard types are a value with a check and nothing more: an IBAN, a
card number, a barcode, an NHS number. They have no operations of their
own. They do what every standard type does
([Every standard type meets one contract](every-standard-type-meets-one-contract.md)):
each is built and checked from text or from its parts, sent, stored and
shown to people. Their rules are how they are built, which `text.parse`
and the type's data entry already cover.

Here a marketplace pays its sellers into the account they type, takes a
patient's NHS number from a referral, and shows a card on file by its
last four digits:

```jadpo
type FormText = Text {}

type NotAnIban = Failure {
    kind: FailureKind.invalid_value
}

type NotAnNhsNumber = Failure {
    kind: FailureKind.invalid_value
}

/// The account a seller is paid into, as they typed it: checked by its
/// country's length and its mod-97 check digits.
function payout_account(raw: FormText) fails NotAnIban -> Iban {
    var account: Iban = attempt Iban(raw) {
        format: NotAnIban
    }
    return account
}

/// A patient's NHS number, as a referral sends it.
function patient_number(raw: FormText) fails NotAnNhsNumber -> NhsNumber {
    var number: NhsNumber = attempt text.parse(raw, as: NhsNumber) {
        malformed: NotAnNhsNumber
    }
    return number
}

/// An account as a statement prints it.
function account_label(account: Iban, viewer: Locale) -> PresentationText {
    return text.format(account, locale: viewer)
}

/// A card on file as a receipt shows it: its last four only.
function card_label(card: PaymentCardNumber, viewer: Locale) -> PresentationText {
    return text.format(card, locale: viewer)
}
```

```jadpo
test "payment and identity numbers are checked as they are built" {
    var account: Iban = attempt payout_account("GB29NWBK60161331926819")
    assert account == Iban("GB29NWBK60161331926819")
    assert payout_account("GB29NWBK60161331926818") fails NotAnIban
    assert payout_account("GB29 NWBK 6016 1331 9268 19") fails NotAnIban
    var patient: NhsNumber = attempt patient_number("9434765919")
    assert patient == NhsNumber("9434765919")
    assert patient_number("9434765918") fails NotAnNhsNumber
}

test "they are shown as they are printed" {
    var account: Iban = Iban("GB29NWBK60161331926819")
    assert account_label(account, Locale.en_gb) == "GB29 NWBK 6016 1331 9268 19"
    assert card_label(PaymentCardNumber("4242424242424242"), Locale.en_gb) == "•••• 4242"
    assert text.format(NhsNumber("9434765919"), locale: Locale.en_gb) == "943 476 5919"
}
```

## Checks beyond a pattern

Most of these types are a `pattern:`. Where a rule is more than a pattern
can say, such as a check digit, the type names one validator:

| Group | Validators beyond a pattern |
|---|---|
| People and contact | `PhoneNumber` (E.164 per country), `PostalCode` (per country), `PostalAddress` and `PersonName` (CLDR order for `text.format`) |
| Places | `CountrySubdivision` against ISO 3166-2, `UnLocode` by its pattern and its country, its list not shipped |
| Payment and identity numbers | `Iban` (mod-97), `PaymentCardNumber` (Luhn), `UkSortCode` with `UkAccountNumber` (modulus check), `AbaRoutingNumber`, `Lei`, `Isin`, `NhsNumber` (mod-11), `UkNationalInsuranceNumber` (prefix rules) |
| Tax and identity documents | `VatNumber`, `TaxId`, `PassportNumber`, `DrivingLicenceNumber` and `IdentityDocument`, each number by its country's published form and check digit (VIES, the OECD's and the EU's TIN pages, each issuer's own), by length where a country publishes none |
| Commerce and logistics | `Ean13`, `UpcA`, `Isbn`, `Sscc` (GS1 check digits), `Vin` (check digit, for North American manufacturers), `ContainerNumber` (ISO 6346) |
| Health | `SnomedConcept` (Verhoeff); `Icd10Code` by pattern, its release not shipped |
| Travel | `IataAirport`, `IcaoAirport`, `IataAirline` by pattern, their lists not shipped |
| Other | `Orcid` (ISO 7064 MOD 11-2), `Mpan` (Elexon's check digit) |

## Payment and identity numbers

Seventeen payment and identity numbers are held as text:
`PaymentCardNumber`, `CardBin`, `PaymentReference`, `InvoiceNumber`,
`Iban`, `Bic`, `UkSortCode`, `UkAccountNumber`, `AbaRoutingNumber`,
`Lei`, `Isin`, `UkUtr`, `UkCompanyNumber`, `UkNationalInsuranceNumber`,
`UsSsn`, `UsEin` and `NhsNumber`.

**Held as text.** Each is a type built on `Text` that the standard
library declares; its stub is `public type Iban = Text {}`. So everything
[Values and types](values-and-types.md) says of text applies:
construction, literals checked while compiling, `attempt`, JSON (a
string), storage (a `text` column) and `==`. Your own type can be built
on one and add rules: `type SterlingIban = Iban { pattern: "GB.*" }`.

Its pattern, matched against the whole text, and its validator are one
check. Text that fails it breaks `format`, as text that isn't an `Email`
does, so there is no new rule name. `text.parse` fails only as
`malformed`. `text.from` is `type.mismatch`, because the value is text
already. None of them is ordered.

**The canonical text is the electronic form,** in upper case with no
spaces or dashes: `GB29NWBK60161331926819`, the sort code `089999`, the
SSN `123456789` and the EIN `123456789`. Only that form is read, as
`text.parse` reads only canonical text everywhere, so a printed IBAN and
lower case are refused. Strip a form's spaces before you build one, with
`text.replace`. An SSN or an EIN with dashes is the printed form.

**Shown to people in the printed form,** whatever the locale:

- an IBAN in groups of four;
- a sort code in pairs with dashes, `08-99-99`;
- a National Insurance number in pairs, `AB 12 34 56 C`;
- an NHS number in groups of 3, 3 and 4, `943 476 5919`;
- an EIN as 2 then 7 digits;
- the others as their canonical text.

The runtime sees only text, so the checker gives `text.format` the name of
the standard type the value is held as, as an argument you don't write.

**Masked by `text.format`, to be safe.** A `PaymentCardNumber` and a
`UsSsn` are shown as their last four only, `•••• 4242` and `•••-••-6789`.
That is as much of a card number as PCI DSS lets a page show, and how the
SSA and the IRS shorten an SSN. Code that must show the whole number has
it as text already.

**Personal data.** Each type names the
[personal data category](marking-personal-fields.md)
its values are in, whatever field holds them:

- `financial` for a card number, an IBAN and a UK account number;
- `government_id` for a National Insurance number, an SSN and a UTR;
- `health` for an NHS number.

[Personal data](personal-data.md) treats a field of such a type as
marked with that category: hashed in logs, kept out of caches when the
category is sensitive, and its reads recorded. Logs carry no values,
only a failure's code and a fault, so nothing writes one in clear. The
[check of what reaches a model](safety-and-privacy.md#personal-data-sent-to-a-model)
already reads the category. A failed assertion in `jadpo test` still
shows the value it compared, which is test data.

**The validators:**

- an IBAN's mod-97, with each country's length from SWIFT's IBAN
  Registry. A country the registry adds later is refused until a release
  adds it, as a new currency is;
- an LEI's ISO 7064 mod 97-10;
- a card number's Luhn check;
- an ISIN's Luhn check over its letters as numbers;
- an ABA routing number's 3-7-1 checksum;
- an NHS number's mod-11. A check digit of 10 is never issued;
- a National Insurance number's prefix rules, from HMRC's NIM39110, with
  a suffix of A to D only;
- a BIC's country, its fifth and sixth letters, which must be a current
  ISO 3166-1 code as `Country` holds them, or Kosovo's user-assigned
  `XK`, which SWIFT issues BICs under as it does IBANs. So `YU`, which was
  withdrawn, is refused.

The rest are patterns: a card BIN of 6 or 8 digits; a payment reference
of up to 18 of Bacs's characters; an invoice number of 1 to 200
characters with no control character and no space at either end; a sort
code; an account number; a UTR; a company number; an EIN; and an SSN,
with the areas, groups and serials the SSA never issues refused.

The UK modulus check needs a sort code and an account number together, so
it belongs to `BankAccount`, under [Bank accounts](bank-accounts.md).

**Test values are published ones** where a scheme publishes them:
SWIFT's registry IBANs, Stripe's test cards, the NHS Number standard's
example and its 999 test range, Vocalink's modulus checking test cases
and GLEIF's LEIs. Where a scheme publishes none, they are made up to the
rules. Each type's entry says which.

Personal data's handling of these categories isn't built yet; see
[What is built](standard-library-built.md).
