Kirjautuminen

Haku

Tehtävät

Koodit: PHP: Viitenumeron laskenta (Suomi ja SEPA)

Kirjoittaja: Metabolix

Kirjoitettu: 24.03.2019 – 24.03.2019

Tagit: teksti, yhteiskunta, hyvää koodia, koodi näytille, vinkki, yleispätevä

Tämä koodivinkki esittelee pankkimaksuissa käytettävän viitenumeron laskentaa.

Suomalainen viitenumero

Suomalainen viitenumero sisältää 4–20 numeroa, joista viimeinen on tarkastusmerkki. Laskentakaavassa kerrotaan viitenumeron valinnaisen osan numerot vuorotellen 1:lla, 3:lla ja 7:lla siten, että viimeinen numero tulee kerrotuksi 7:lla. Kertolaskujen tulokset lasketaan yhteen. Tarkastusmerkki valitaan siten, että kun se lisätään summaan, summasta tulee jaollinen 10:llä.

Viitteen alku:  8    5    5    8    4    8    2
Kertoimet:      7    1    3    7    1    3    7
Tulos:         56 +  5 + 15 + 56 +  4 + 24 + 14  = 174
Tarkastusmerkki: 180 - 174  =  (10 - 174 % 10) % 10  =  6
Viite: 855 84826
<?php
# Moduuli suomalaisten viitenumeroiden käsittelyyn.
class SuomalainenViite {
	# Luo kokonaisen viitteen (alku + tarkiste) alkuosan perusteella.
	public static function luo($alku, $ryhmittely = true) {
		# Poistetaan muut kuin numerot.
		$alku = preg_replace("/[^0-9]*/s", "", $alku);

		# Tarkastusmerkin laskenta.
		$summa = 0;
		$l = strlen($alku);
		for ($i = 0; $i < $l; ++$i) {
			$summa += substr($alku, -1 - $i, 1) * [7, 3, 1][$i % 3];
		}
		$merkki = (10 - $summa % 10) % 10;
		$viite = $alku . $merkki;

		return $ryhmittely ? self::ryhmittele($viite) : $viite;
	}

	# Ryhmittelee viitenumeron viiden numeron sarjoihin lopusta alkuun.
	public static function ryhmittele($viite) {
		$viite = preg_replace("/[^0-9]*/s", "", $viite);
		return strrev(trim(chunk_split(strrev($viite), 5, " ")));
	}

	# Tarkastaa, onko viite kelvollinen.
	public static function tarkasta($viite) {
		# Vääriä merkkejä?
		if (strspn($viite, "0123456789 ") != strlen($viite)) {
			return false;
		}
		$viite = str_replace(" ", "", $viite);
		# Väärä pituus?
		if (strlen($viite) > 20 || strlen($viite) < 4) {
			return false;
		}
		# Väärä tarkistusmerkki?
		return $viite == self::luo(substr($viite, 0, -1), false);
	}
}

Kansainvälinen viitenumero

Kansainvälistä viitenumeroa käytetään esimerkiksi SEPA-maksuissa. Viitenumeron alussa ovat merkit RF ja kaksinumeroinen tarkiste, ja tämän jälkeen voi tulla enintään 21 omavalintaista merkkiä. Suomessa käytetään loppuosana yleensä numeroista 0–9 muodostuvaa suomalaista viitenumeroa, mutta myös kirjaimet A–Z ja a–z ovat sallittuja. Tarkiste muodostetaan seuraavasti:

  1. Otetaan viitteen valinnainen osa, esimerkiksi ”123ABCZ”.
  2. Poistetaan tästä välit ja muutetaan kirjaimet isoiksi.
  3. Lisätään loppuun merkit RF00.
  4. Muutetaan kirjaimet lukuarvoiksi siten, että A = 10, B = 11, ..., Z = 35. Esimerkiksi ”123ABCZRF00” muuttuu muotoon ”12310111235271500”.
  5. Lasketaan jakojäännös 97:llä ja vähennetään tämä 98:sta. Esimerkiksi 12310111235271500 jaettuna 97:llä jättää 63, ja 98 - 63 on 35.
  6. Jos saatu tulos on yksinumeroinen, lisätään siihen etunolla.
  7. Viitenumeroksi tulee siis ”RF” + ”35” + ”123ABCZ” = ”RF35 123A BCZ”.

Kansainvälistä viitenumeroa koskee standardi ISO 11649.

<?php
# Moduuli kansainvälisten viitenumeroiden käsittelyyn (ISO_11649).
class SepaViite {
	# Siivoaa viitteestä ylimääräiset välimerkit.
	public static function siivoa($viite) {
		return strtoupper(preg_replace("/[^0-9a-zA-Z]/s", "", $viite));
	}

	# Muuttaa viitteen numeeriseen muotoon jakojäännöstä varten.
	public static function numeeriseksi($viite) {
		return preg_replace_callback(
			"/([0-9])|([A-Z])|([a-z])|./s",
			function($m) {
				if (!empty($m[2])) return ord($m[2]) - ord("A") + 10;
				if (!empty($m[3])) return ord($m[3]) - ord("a") + 10;
				return $m[1] ?? "";
			},
			$viite
		);
	}

	# Jakojäännös 97:llä pitkästä luvusta onnistuu esim. GMP-kirjastolla.
	public static function mod97($luku) {
		return (int) gmp_mod($luku, 97);
	}

	# Luo kokonaisen viitteen (RF + tarkiste) loppuosan perusteella.
	public static function luo($loppuosa, $ryhmittely = true) {
		$loppuosa = self::siivoa($loppuosa);
		$luku = self::numeeriseksi($loppuosa . "RF00");
		$tarkiste = sprintf("%02d", (98 - self::mod97($luku)));
		$viite = "RF" . $tarkiste . $loppuosa;
		return $ryhmittely ? self::ryhmittele($viite) : $viite;
	}

	# Ryhmittelee viitteen neljän merkin joukkoihin alusta alkaen.
	public static function ryhmittele($viite) {
		$viite = self::siivoa($viite);
		return trim(chunk_split($viite, 4, " "));
	}

	# Tarkastaa, onko viite kelvollinen.
	public static function tarkasta($viite) {
		$viite = self::siivoa($viite);
		if (strlen($viite) > 25 || strlen($viite) <= 4) {
			return false;
		}
		$luku = self::numeeriseksi(substr($viite, 4) . substr($viite, 0, 4));
		return self::mod97($luku) == 1;
	}
}

Esimerkki

Suomessa yleensä halutaan käyttää suomalaista viitenumeroa, joka kuitenkin sitten kansainväliselle asiakkaalle voi olla tarpeen antaa kansainvälisessä muodossa. Yleensä viitenumero muodostetaan esimerkiksi laskun numerosta. Näin se käy:

<?php
require_once "SuomalainenViite.php";
require_once "SepaViite.php";

$lasku = "881628761018";
$suomiviite = SuomalainenViite::luo($lasku);
$sepaviite = SepaViite::luo($suomiviite);
echo "Suomi: {$suomiviite}\n";
echo "SEPA: {$sepaviite}\n";

Kirjoita kommentti

Muista lukea kirjoitusohjeet.
Tietoa sivustosta