Tuesday, September 02, 2008

Credit Card number validator for Delphi

We are implementing parts of a global electronic payment solution and one of the mandatory features was to verify credit card numbers. This is done using the Luhn algorithm. I was expecting to find a wide variety of implementations on our sexy Delphi language but to my surprise there were very few hits on my search.

So, for others that may needed, I'm including this small routine that validates the credit card number (not the card secure code, nor the expiration date). There are several implementations on the web that based on the card prefix digits you can determine the kind of card you are looking at (VISA, MC, Dinners, etc.) That kind of inferences are not reliable for a global solution so the decision was made to drop that auto detection.

function ValidateCreditCardNumber(CreditCardNumber:string):boolean;

Const
DigitsAllowed = ['0'..'9'];
MaxCCSize = 19;
MinCCSize = 13;

var
i : integer;
CleanCardNumber: string;
digit: Integer;
CheckSum: Integer; { Holds the value of the operation }
Flag: Boolean; { used to indicate when ready }
Counter: Integer; { index counter }
PartNumber: string; { used to extract each digit of number }
Number: Integer; { used to convert each digit to integer}


Begin
CleanCardNumber:='';
digit:=0;

// Remove any non numeric value
for I := 1 to Length(CreditCardNumber) do
Begin
if CreditCardNumber[i] in DigitsAllowed then
CleanCardNumber:= CleanCardNumber + CreditCardNumber[i];
End;

// Check for valid card length number
if (Length(CleanCardNumber)MaxCCSize) then
Begin
Result:= False;
Exit;
End;

// get the starting value for our counter
Counter := Length(CleanCardNumber);
CheckSum := 0;
PartNumber := '';
Number := 0;
Flag := false;

while (Counter >= 1) do
begin
// get the current digit
PartNumber := Copy(CleanCardNumber, Counter, 1);
Number := StrToInt(PartNumber); // convert to integer
if (Flag) then // only do every other digit
begin
Number := Number * 2;
if (Number >= 10) then
Number := Number - 9;
end;
CheckSum := CheckSum + Number;

Flag := not (Flag);

Counter := Counter - 1;
end;

result := ((CheckSum mod 10) = 0);

End;

This implementation is based on a 1996 VB Code migration from Shawn Wilson.

3 comments:

Anonymous said...

My implementation is somewhat shortrer, but does not check the length.


function CheckLuhn(s:string):Boolean;
var
alt,err:boolean;
i,sum:integer; d:byte;
const luhn_dbl:array[0..9] of byte=(0,2,4,6,8,1,3,5,7,9);
begin
Result:=False; err:=false; Sum:=0;
alt:=(length(s) mod 2=0);
for i := 1 to length(s) do
begin
d:=StrToIntDef(s[i],-1);
if d=-1 then
err:=true
else
if alt then sum:=sum+luhn_dbl[d] else sum:=sum+d;
alt:=not alt;
end;
Result:= (sum mod 10 =0) and (not err);
end;

Trevor said...
This comment has been removed by the author.
Trevor said...

Esteban,

Apologies, I posted a comment about using the prefix of the card to determine what information to verify and then noticed your comment that this wasn't acceptable for a global solution - so that is the reason I deleted the post.

Having said that, we have just had our new mail order system accredited by our authorising company and they are delighted that we make use of the IIN tables to pre-validate data before requesting authorisation from them. In fact they did say they wished more people would ;-) !

Apologies once again for clogging up you blog without first fully reading your comments,

Trevor

A painless self-hosted Git service

Remember how a part of my NAS setup was to host my own Git server? Well that forced me to review options and I stumble into Gitea .  A extr...