CONTRÔLE DE LA CONFORMITÉ D'UN NUMÉRO DE CARTE BANCAIRE (ALGORITHME DE LUHN)

Messages postés
8
Date d'inscription
jeudi 3 août 2006
Statut
Membre
Dernière intervention
18 février 2009
- - Dernière réponse : verdy_p
Messages postés
203
Date d'inscription
vendredi 27 janvier 2006
Statut
Membre
Dernière intervention
29 janvier 2019
- 17 févr. 2009 à 11:11
Cette discussion concerne un article du site. Pour la consulter dans son contexte d'origine, cliquez sur le lien ci-dessous.

https://codes-sources.commentcamarche.net/source/49262-controle-de-la-conformite-d-un-numero-de-carte-bancaire-algorithme-de-luhn

Afficher la suite 
fs074995
Messages postés
8
Date d'inscription
jeudi 3 août 2006
Statut
Membre
Dernière intervention
18 février 2009
-
Bonjour
Mais pourquoi faire compliqué si cela peut être simple
origine: wiki alogarithme de luhn

01.//Fonction algorithme de Luhn
02.function isLuhnNum($num){
03. //longueur de la chaine $num
04. $length = strlen($num);
05. //resultat de l'addition de tous les chiffres
06. $tot = 0;
07. for($i=$length-1;$i>=0;$i--)
08. {
09. $digit = substr($num, $i, 1);
10. if ((($length - $i) / 2) == 0){
11. $digit = $digit*2;
12. if ($digit>9){
13. $digit = $digit-9;
14. }
15. }
16. $tot += $digit;
17. }
18. return (($tot / 10) == 0);
19.}

Ceci dit cela peut être utile à beaucoup Merci
verdy_p
Messages postés
203
Date d'inscription
vendredi 27 janvier 2006
Statut
Membre
Dernière intervention
29 janvier 2019
-
Désolé, FS074995, mais ton code est *presque* bon, mais incorrect (tu a mis une division flottante par 2 au lieu d'un modulo 2 entier pour tester la parité de la position du chiffre par rapport à la fin du nombre, ainsi qu'une division flottante par 10 au lieu d'un modulo 10 pour tester la somme finale). Ton résultat sera donc faux.

Voici une version corrigée (qui en plus sait ignorer les séparateurs comme les blancs, points ou tirets et ne tient compte que des chiffres):
//Fonction algorithme de Luhn
function isLuhnNum($num) {
//longueur de la chaine $num
$len = strlen($num);
$pair = true;
$tot = 0;
for ($i = 0; $i < $len; $i++)
if (($digit = substr($num, $len - $i - 1, 1))
>= '0' && $digit <= '9')
$tot += $digit +
( ($pair = ! $pair)
? $digit - (($digit >= 5) ? 9 : 0)
: 0
);
return ($tot % 10) == 0;
}

Note: la variable $pair est nécessaire ci-dessus si on veut filter les non-chiffres dans la chaîne.

En revanche, si la chaîne est assurée de n'avoir plus QUE des chiffres, alors $pair est inutile et il suffit juste de tester ($i & 1) == 0 ci-dessus:

function isLuhnNum($num) {
//longueur de la chaine $num
$len = strlen($num);
$tot = 0;
for ($i = 0; $i < $len; $i++) $tot +($digit substr($num, $len - $i - 1, 1)
+ ( ($i & 1) == 0 ?
? $digit - (($digit >= 5) ? 9 : 0)
: 0;
return ($tot % 10) == 0;
}

L'algo de Luhn complet devrait aussi prendre en compte et accepter les lettres (pour vérifier les numéros de comptes de la banque postale par exemple) : à chaque lettre correspond un chiffre équivalent (pour l'algorithme du LUHN uniquement, mais significatif autrement), fonction de son rang dans l'alphabet:
A=0, B=1, C=2, ..., H=9,
I=0, J=1, K=2, ..., S=9,
T=0, U=1, V=1, ..., Z=4
verdy_p
Messages postés
203
Date d'inscription
vendredi 27 janvier 2006
Statut
Membre
Dernière intervention
29 janvier 2019
-
Note: dans la seconde version, j'ai oublié de coller 2 parenthèses ci-dessus, et en plus j'ai inversé le test de parité: le dernier chiffre n'est pas doublé (c'est l'avant-dernier qui l'est) :
$tot +($digit substr($num, $len - $i - 1, 1))
+ ( ($i & 1) != 0 ?
? $digit - (($digit >= 5) ? 9 : 0)
: 0);

La première version en revanche (qui teste et élimine les séparateurs) était correcte et est plus proche de celle visant à supporter aussi les lettres à convertir en chiffre.
Seb33300
Messages postés
16
Date d'inscription
dimanche 9 avril 2006
Statut
Membre
Dernière intervention
25 janvier 2011
-
En fait au départ je comptais utiliser un 2eme algo de contrôle à la suite pour plus de fiabilité et du coup je voulais bien séparer la vérification de la chaine / contrôle 1 / contrôle 2

Et finalement je n'ai pas mis en place le 2nd contrôle.
verdy_p
Messages postés
203
Date d'inscription
vendredi 27 janvier 2006
Statut
Membre
Dernière intervention
29 janvier 2019
-
Seb, il aurait fallu quand même éviter les deux bogues de la division par 2 et par 10. Pas la peine de poster ici un code tapé en vitesse qui donne un résultat faux et ne décrit pas l'alogo correctement (alors qu'il est documenté correctement partout ailleurs).
Seb33300
Messages postés
16
Date d'inscription
dimanche 9 avril 2006
Statut
Membre
Dernière intervention
25 janvier 2011
-
Quelle division par 2 et par 10 ?
verdy_p
Messages postés
203
Date d'inscription
vendredi 27 janvier 2006
Statut
Membre
Dernière intervention
29 janvier 2019
-
tu as mis « / 2 » et « / 10 » au lieu de « % 2 » et « % 10 » !
Seb33300
Messages postés
16
Date d'inscription
dimanche 9 avril 2006
Statut
Membre
Dernière intervention
25 janvier 2011
-
Quelle ligne... ?

Plutôt que de dire n'importe quoi tu ferais mieux de lire et tester le code avant de me cracher dessus.
verdy_p
Messages postés
203
Date d'inscription
vendredi 27 janvier 2006
Statut
Membre
Dernière intervention
29 janvier 2019
-
Lignes 10 et 18, il faut tout te dire ! Franchement il n'y a pas 10 000 endroits. Quand on ne veut pas regarder...

C'est toi qui ne veux pas lire ton propre texte, et qui "crache". Reconnais que tu t'es trompé (moi-même j'ai aussitôt admis m'être trompé dans le premier jet dans le deuxième code édité un peu trop vite, en omettant deux parenthèses, mais je me suis corrigé; je peux aussi faire des erreurs, mais ton problème est que tu n'admets pas les tiennes).

Ton code est faux car il n'acceptera QUE un numéro entièrement fait de zéros. Pour tous les autres, la division du chiffre par 12 en PH est une division flottante qui retournera un entier un un entier plus un demi, donc différente de zéro (donc le code dans le "if" ne sera pas exécuté) et le chiffre ne sera pas doublé. Tous les chiffres seront donc simplement ajoutés les uns aux autres dans la boucle (aucun ne sera doublé sauf les zéros où c'est sans effet). Dans la dernière ligne la division par 10 est également flottante et divisera la somme des chiffres par 10. Cette division ne sera nulle que si la somme des chiffres est elle-même nulle. De fait ton code ne retourne 0 (OK) que si la somme des chiffres (produite par la boucle) est nulle, donc si tous les chiffres sont nuls. CQFD.

Ton code est donc inutilisable car tu as confondu la division "/" avec le modulo "%" demandé dans les deux lignes: c'est bien un modulo qu'il faut utiliser pour tester la parité (on peut aussi utiliser "x & 1" au lieu de "x % 2"), et pour tester si la somme est un multiple de 10; la division est hors de propos.
verdy_p
Messages postés
203
Date d'inscription
vendredi 27 janvier 2006
Statut
Membre
Dernière intervention
29 janvier 2019
-
Excuse moi Seb, mais tu te trompes d'interlocuteur (et je me trompe aussi): mes commentaires postés ici ne concernais QUE le code de la réponse donnée par FS074995: c'est ce code qui est faux, et c'est LUI (enfin plutôt son code) que j'ai critiqué, pas toi. D'ailleurs je l'avais indiqué très explicitement dans ma première réponse à ce sujet en le nommant dans le deuxième mot. Mes corrections concernaient SON code, pas le tien qui est correct (même s'il est un peu lourd et n'explote pas complètement la syntaxe PHP et contient plusieurs RETURN).

Dans la correction que j'ai postée, je ne teste pas la longueur (16) car je ne suppose pas que l'algo est réservé aux numéros de cartes bancaires (à ce sujet, il y a des numéros de carte à 13 ou 15 chiffres, et même des cartes de crédit à 20 chiffres...), mais il fonctionne aussi pour certains numéros de comptes bancaires (mais pas les RIB français qui utilisent une somme modulo 97, des chiffres groupés par 6 depuis la gauche, la clé RIB à deux chiffres étant entre 01 et 97 où 97 remplace 00 dans les clés réelles, ce qui ne change rien au modulo 97, ni les numéros IBAN qui utilisent un algorithme modifié là aussi pour accepter les lettres).
verdy_p
Messages postés
203
Date d'inscription
vendredi 27 janvier 2006
Statut
Membre
Dernière intervention
29 janvier 2019
-
Concernant les longueurs de numéros, on a aussi 17 chiffres (6 pour l'émetteur, plus 10 pour le numéro de carte, plus 1 pour la clé) dans les cartes S'Miles par exemple.

Pour les cartes de crédit revolving comme Aurore, on a un autre format à 19 chiffres (6 pour l'émetteur, plus 10 pour le numéro de compte carte, plus 3 pour l'index de la carte et la clé); je n'ai pas vérifié si l'algo de Luhn était utilisé pour ces cartes et il est probable qu'il y a d'autres sommes de contrôle cachées dans certaines composantes, comme c'est aussi le cas pour les numéros de compte bancaire

Pour les cartes CB émises par certaines banques on a le même problème car 16 chiffres sont insuffisants pour éviter l'utilisation abusive ou accidentelle de codes existants: c'est la raison pour laquelle les numéros de carte à 16 chiffres ont maintenant une extension à 3 ou 4 chiffres imprimé derrière la carte dans la zone de signature, un autre numéro de contrôle de validité destiné à renforcer le code de Luhn et à vérifier la période de validité d'une carte émise quand il y en aura plusieurs pour le même compte).