Проверить IBAN PHP

При разработке новой платформы мы постарались интегрировать номера IBAN. Мы должны убедиться, что IBAN проверен и IBAN, сохраненный в базе данных, всегда корректен. Итак, что было бы правильным способом проверить число?

10 ответов

Решение

Как объяснила логика в моем другом вопросе, я попытался создать функцию сам. Основываясь на логике, объясненной в статье в википедии, найдите подходящую функцию ниже. Проверка конкретной страны.

Будет ли это соответствовать

http://en.wikipedia.org/wiki/International_Bank_Account_Number

function checkIBAN($iban)
{
    $iban = strtolower(str_replace(' ','',$iban));
    $Countries = array('al'=>28,'ad'=>24,'at'=>20,'az'=>28,'bh'=>22,'be'=>16,'ba'=>20,'br'=>29,'bg'=>22,'cr'=>21,'hr'=>21,'cy'=>28,'cz'=>24,'dk'=>18,'do'=>28,'ee'=>20,'fo'=>18,'fi'=>18,'fr'=>27,'ge'=>22,'de'=>22,'gi'=>23,'gr'=>27,'gl'=>18,'gt'=>28,'hu'=>28,'is'=>26,'ie'=>22,'il'=>23,'it'=>27,'jo'=>30,'kz'=>20,'kw'=>30,'lv'=>21,'lb'=>28,'li'=>21,'lt'=>20,'lu'=>20,'mk'=>19,'mt'=>31,'mr'=>27,'mu'=>30,'mc'=>27,'md'=>24,'me'=>22,'nl'=>18,'no'=>15,'pk'=>24,'ps'=>29,'pl'=>28,'pt'=>25,'qa'=>29,'ro'=>24,'sm'=>27,'sa'=>24,'rs'=>22,'sk'=>24,'si'=>19,'es'=>24,'se'=>24,'ch'=>21,'tn'=>24,'tr'=>26,'ae'=>23,'gb'=>22,'vg'=>24);
    $Chars = array('a'=>10,'b'=>11,'c'=>12,'d'=>13,'e'=>14,'f'=>15,'g'=>16,'h'=>17,'i'=>18,'j'=>19,'k'=>20,'l'=>21,'m'=>22,'n'=>23,'o'=>24,'p'=>25,'q'=>26,'r'=>27,'s'=>28,'t'=>29,'u'=>30,'v'=>31,'w'=>32,'x'=>33,'y'=>34,'z'=>35);

    if(strlen($iban) == $Countries[substr($iban,0,2)]){

        $MovedChar = substr($iban, 4).substr($iban,0,4);
        $MovedCharArray = str_split($MovedChar);
        $NewString = "";

        foreach($MovedCharArray AS $key => $value){
            if(!is_numeric($MovedCharArray[$key])){
                $MovedCharArray[$key] = $Chars[$MovedCharArray[$key]];
            }
            $NewString .= $MovedCharArray[$key];
        }

        if(bcmod($NewString, '97') == 1)
        {
            return true;
        }
        else{
            return false;
        }
    }
    else{
        return false;
    }   
}

Небольшая модификация ответа @PeterFox, включая поддержку bcmod() когда bcmath не доступен,

<?php

function isValidIBAN ($iban) {

  $iban = strtolower($iban);
  $Countries = array(
    'al'=>28,'ad'=>24,'at'=>20,'az'=>28,'bh'=>22,'be'=>16,'ba'=>20,'br'=>29,'bg'=>22,'cr'=>21,'hr'=>21,'cy'=>28,'cz'=>24,
    'dk'=>18,'do'=>28,'ee'=>20,'fo'=>18,'fi'=>18,'fr'=>27,'ge'=>22,'de'=>22,'gi'=>23,'gr'=>27,'gl'=>18,'gt'=>28,'hu'=>28,
    'is'=>26,'ie'=>22,'il'=>23,'it'=>27,'jo'=>30,'kz'=>20,'kw'=>30,'lv'=>21,'lb'=>28,'li'=>21,'lt'=>20,'lu'=>20,'mk'=>19,
    'mt'=>31,'mr'=>27,'mu'=>30,'mc'=>27,'md'=>24,'me'=>22,'nl'=>18,'no'=>15,'pk'=>24,'ps'=>29,'pl'=>28,'pt'=>25,'qa'=>29,
    'ro'=>24,'sm'=>27,'sa'=>24,'rs'=>22,'sk'=>24,'si'=>19,'es'=>24,'se'=>24,'ch'=>21,'tn'=>24,'tr'=>26,'ae'=>23,'gb'=>22,'vg'=>24
  );
  $Chars = array(
    'a'=>10,'b'=>11,'c'=>12,'d'=>13,'e'=>14,'f'=>15,'g'=>16,'h'=>17,'i'=>18,'j'=>19,'k'=>20,'l'=>21,'m'=>22,
    'n'=>23,'o'=>24,'p'=>25,'q'=>26,'r'=>27,'s'=>28,'t'=>29,'u'=>30,'v'=>31,'w'=>32,'x'=>33,'y'=>34,'z'=>35
  );

  if (strlen($iban) != $Countries[ substr($iban,0,2) ]) { return false; }

  $MovedChar = substr($iban, 4) . substr($iban,0,4);
  $MovedCharArray = str_split($MovedChar);
  $NewString = "";

  foreach ($MovedCharArray as $k => $v) {

    if ( !is_numeric($MovedCharArray[$k]) ) {
      $MovedCharArray[$k] = $Chars[$MovedCharArray[$k]];
    }
    $NewString .= $MovedCharArray[$k];
  }
  if (function_exists("bcmod")) { return bcmod($NewString, '97') == 1; }

  // http://au2.php.net/manual/en/function.bcmod.php#38474
  $x = $NewString; $y = "97";
  $take = 5; $mod = "";

  do {
    $a = (int)$mod . substr($x, 0, $take);
    $x = substr($x, $take);
    $mod = $a % $y;
  }
  while (strlen($x));

  return (int)$mod == 1;
}

Принятый ответ не является предпочтительным способом проверки, спецификация диктует следующее:

 1. Check that the total IBAN length is correct as per the country. If not, the IBAN is invalid
 2. Replace the two check digits by 00 (e.g. GB00 for the UK)
 3. Move the four initial characters to the end of the string
 4. Replace the letters in the string with digits, expanding the string as necessary, such that A or a = 10,
    B or b = 11, and Z or z = 35. Each alphabetic character is therefore replaced by 2 digits
 5. Convert the string to an integer (i.e. ignore leading zeroes)
 6. Calculate mod-97 of the new number, which results in the remainder
 7. Subtract the remainder from 98, and use the result for the two check digits. If the result is a single digit
   number, pad it with a leading 0 to make a two-digit number

Я написал класс, который проверяет, форматирует и анализирует строки в соответствии со спецификацией. Надеюсь, это поможет сэкономить время на их создание. Код найден здесь:

https://gist.github.com/esserj/a54ffd11182417cf920d

Извините, я пока не могу комментировать из-за низкой репутации, поэтому новая ссылка на user3733632s отвечает:

https://github.com/globalcitizen/php-iban

(проект больше не размещается в коде Google по состоянию на март 2015 года, и после некоторой очистки проект получит новую версию.)

Последний список стран (2020-04-07):

$countries = [
    'ad' => 24,
    'ae' => 23,
    'al' => 28,
    'at' => 20,
    'az' => 28,
    'ba' => 20,
    'be' => 16,
    'bg' => 22,
    'bh' => 22,
    'br' => 29,
    'by' => 28,
    'ch' => 21,
    'cr' => 21,
    'cy' => 28,
    'cz' => 24,
    'de' => 22,
    'dk' => 18,
    'do' => 28,
    'ee' => 20,
    'es' => 24,
    'fi' => 18,
    'fo' => 18,
    'fr' => 27,
    'gb' => 22,
    'ge' => 22,
    'gi' => 23,
    'gl' => 18,
    'gr' => 27,
    'gt' => 28,
    'hr' => 21,
    'hu' => 28,
    'ie' => 22,
    'il' => 23,
    'is' => 26,
    'it' => 27,
    'jo' => 30,
    'kw' => 30,
    'kz' => 20,
    'lb' => 28,
    'li' => 21,
    'lt' => 20,
    'lu' => 20,
    'lv' => 21,
    'mc' => 27,
    'md' => 24,
    'me' => 22,
    'mk' => 19,
    'mr' => 27,
    'mt' => 31,
    'mu' => 30,
    'nl' => 18,
    'no' => 15,
    'pk' => 24,
    'pl' => 28,
    'ps' => 29,
    'pt' => 25,
    'qa' => 29,
    'ro' => 24,
    'rs' => 22,
    'sa' => 24,
    'se' => 24,
    'si' => 19,
    'sk' => 24,
    'sm' => 27,
    'tl' => 23,
    'tn' => 24,
    'tr' => 26,
    'ua' => 29,
    'vg' => 24,
    'xk' => 20,
];

https://ru.wikipedia.org/wiki/IBAN

Эта функция проверяет IBAN и требует активации GMP http://php.net/manual/en/book.gmp.php.

function checkIban($string){
    $to_check = substr($string, 4).substr($string, 0,4);
    $converted = '';
    for ($i = 0; $i < strlen($to_check); $i++){
        $char = strtoupper($to_check[$i]);
        if(preg_match('/[0-9A-Z]/',$char)){
            if(!preg_match('/\d/',$char)){
                $char = ord($char)-55;
            }
            $converted .= $char;
        }
    }
    return (strlen($converted) > 0 && gmp_strval(gmp_mod($converted, "97")) == 1);
}

наслаждаться!

Функция с самым высоким рейтингом не работает.

Просто попробуйте строку с '%' в нем...

Я использую это:

function checkIBAN($iban) {

// Normalize input (remove spaces and make upcase)
$iban = strtoupper(str_replace(' ', '', $iban));

if (preg_match('/^[A-Z]{2}[0-9]{2}[A-Z0-9]{1,30}$/', $iban)) {
    $country = substr($iban, 0, 2);
    $check = intval(substr($iban, 2, 2));
    $account = substr($iban, 4);

    // To numeric representation
    $search = range('A','Z');
    foreach (range(10,35) as $tmp)
        $replace[]=strval($tmp);
    $numstr=str_replace($search, $replace, $account.$country.'00');

    // Calculate checksum
    $checksum = intval(substr($numstr, 0, 1));
    for ($pos = 1; $pos < strlen($numstr); $pos++) {
        $checksum *= 10;
        $checksum += intval(substr($numstr, $pos,1));
        $checksum %= 97;
    }

    return ((98-$checksum) == $check);
} else
    return false;
}

Я нашел это решение в классе проверки CakePHP 3.7. Простая красивая реализация php.

/**
 * Check that the input value has a valid International Bank Account Number IBAN syntax
 * Requirements are uppercase, no whitespaces, max length 34, country code and checksum exist at right spots,
 * body matches against checksum via Mod97-10 algorithm
 *
 * @param string $check The value to check
 *
 * @return bool Success
 */
public static function iban($check)
{
    if (!preg_match('/^[A-Z]{2}[0-9]{2}[A-Z0-9]{1,30}$/', $check)) {
        return false;
    }

    $country = substr($check, 0, 2);
    $checkInt = intval(substr($check, 2, 2));
    $account = substr($check, 4);
    $search = range('A', 'Z');
    $replace = [];
    foreach (range(10, 35) as $tmp) {
        $replace[] = strval($tmp);
    }
    $numStr = str_replace($search, $replace, $account . $country . '00');
    $checksum = intval(substr($numStr, 0, 1));
    $numStrLength = strlen($numStr);
    for ($pos = 1; $pos < $numStrLength; $pos++) {
        $checksum *= 10;
        $checksum += intval(substr($numStr, $pos, 1));
        $checksum %= 97;
    }

    return ((98 - $checksum) === $checkInt);
}

я бы предложил небольшую оптимизацию расчета контрольной суммы...

      function calculateIBANChecksum($CIBAN){
    $CX = str_split($CIBAN);
    $checksum = $CX[0];
    array_shift($CX);
    foreach ($CX as $pos => $DIGIT) {
        $checksum *= 10;
        $checksum += $DIGIT * 1;
        $checksum %= 97;
    }
    return  (98 - $checksum);
}

В Google Code находится проект валидатора php-iban с открытым исходным кодом: https://code.google.com/p/php-iban/

Другие вопросы по тегам