2005. április 21., csütörtök

Credit Card Validation (2)


Problem/Question/Abstract:

How can I know if a number is a valid credit card number?

Answer:

The following unit can be used to make credit card verification

unit Creditc;

{*****************************************************************************

Credit Card Number Validator Unit for Delphi

Version: 1.1
Date: December 20, 1996

This unit is based on the public domain program ccard by Peter Miller.
It is released to the public for free of charge use, but the author
reserves all rights.

copyright 1996 by Shawn Wilson Harvell ( shawn@inet.net )

usage:

Add this unit to the uses clause of any unit that needs access to the
validation function.

IsValidCreditCardNumber( CardNumber, ReturnMessage ) returns Boolean

for example, use it in an if statement that Messages user if invalid.

CardNumber is a string containing the number that you want to validate
ReturnMessage is a string where the function can place any messages it
may return ( meaning that it will overwrite whatever is in it )

returns true if valid, false otherwise.

dashes and space in the input value are taken care of by the function,
if other characters are possible, you may wish to remove them as well.
The function RemoveChar will take care of this quite easily, simply
pass the input string and the char you wish to delete.

Users are free to modify this unit for their own use, but in
distributing you should advise all users of the changes made.

Use this unit at your own risk, it does not come with any warranties
either express or implied. Damages resulting from the use of this
unit are the sole responsibility of the user.

This should work as is for Delphi versions 1 and 2, some slight
modifications may be necessary for Turbo Pascal ( mainly due to use to
conversion functions from the SysUtils unit ).

If you do find this useful, have any comments or suggestions, please
drop the author an email at shawn@inet.net

Revision History

version 1.1 -- December 20, 1996
blooper with Discover cards, added their length mask to the "database"

version 1.0 -- October 26, 1996
initial release

*****************************************************************************}

interface

uses SysUtils;

function IsValidCreditCardNumber(CardNumber: string; var MessageText: string): Boolean;

implementation

const
  CardPrefixes: array[1..19] of string =
  ('2014', '2149', '300', '301', '302',
    '303', '304', '305', '34', '36', '37',
    '38', '4', '51', '52', '53', '54', '55', '6011');

  CardTypes: array[1..19] of string =
  ('enRoute',
    'enRoute',
    'Diner Club/Carte Blanche',
    'Diner Club/Carte Blanche',
    'Diner Club/Carte Blanche',
    'Diner Club/Carte Blanche',
    'Diner Club/Carte Blanche',
    'Diner Club/Carte Blanche',
    'American Express',
    'Diner Club/Carte Blanche',
    'American Express',
    'Diner Club/Carte Blanche',
    'Visa',
    'MasterCard',
    'MasterCard',
    'MasterCard',
    'MasterCard',
    'MasterCard',
    'Discover');

function RemoveChar(const Input: string; DeletedChar: Char): string;
var
  Index: Word; { counter variable }
begin
  { all this function does is iterate through string looking for char, if found }
  { it deletes it }
  Result := Input;
  for Index := Length(Result) downto 1 do
    if Result[Index] = DeletedChar then
      Delete(Result, Index, 1);
end;

function ShiftMask(Input: Integer): Integer;
begin
  { simply a wrapper for this left bit shift operation }
  result := (1 shl (Input - 12));
end;

function ConfirmChecksum(CardNumber: string): Boolean;
var
  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

  {**************************************************************************
  This is probably the most confusing part of the code you will see, I know
  that it is some of the most confusing I have ever seen. Basically, this
  function is extracting each digit of the number and subjecting it to the
  checksum formula established by the credit card companies. It works from
  the end to the front.
  **************************************************************************}

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

  while (Counter >= 1) do
  begin
    { get the current digit }
    PartNumber := Copy(CardNumber, 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;

function GetMask(CardName: string): Integer;
begin
  { the default case }
  result := 0;

  if (CardName = 'MasterCard') then
    result := ShiftMask(16);
  if (CardName = 'Visa') then
    result := (ShiftMask(13) or ShiftMask(16));
  if (CardName = 'American Express') then
    result := ShiftMask(15);
  if (CardName = 'Diner Club/Carte Blanche') then
    result := ShiftMask(14);
  if (CardName = 'Discover') then
    result := ShiftMask(16);

end;

function IsValidCreditCardNumber(CardNumber: string; var MessageText: string):
  Boolean;
var
  StrippedNumber: string; { used to hold the number bereft of extra chars }
  Index: Integer; { general purpose counter for loops, etc }
  TheMask: Integer; { number we will use for the mask }
  FoundIt: Boolean; { used to indicate when something is found }
  CardName: string; { stores the name of the type of card }
  PerformChecksum: Boolean; { the enRoute type of card doesn't get it }
begin

  { first, get rid of spaces, dashes }
  StrippedNumber := RemoveChar(CardNumber, ' ');
  StrippedNumber := RemoveChar(StrippedNumber, '-');

  { if the string was zero length, then OK too }
  if (StrippedNumber = '') then
  begin
    result := true;
    exit;
  end;

  { initialize return variables }
  MessageText := '';
  result := true;

  { set our flag variable }
  FoundIt := false;

  { check for invalid characters right off the bat }
  for Index := 1 to Length(StrippedNumber) do
  begin
    case StrippedNumber[Index] of
      '0'..'9': FoundIt := FoundIt; { non op in other words }
    else
      MessageText := 'Invalid Characters in Input';
      result := false;
      exit;
    end;
  end;

  { now let's determine what type of card it is }
  for Index := 1 to 19 do
  begin
    if (Pos(CardPrefixes[Index], StrippedNumber) = 1) then
    begin
      { we've found the right one }
      FoundIt := true;
      CardName := CardTypes[Index];
      TheMask := GetMask(CardName);
    end;
  end;

  { if we didn't find it, indicates things are already ary }
  if (not FoundIt) then
  begin
    CardName := 'Unknown Card Type';
    TheMask := 0;
    MessageText := 'Unknown Card Type ';
    result := false;
    exit;
  end;

  { check the length }
  if ((Length(StrippedNumber) > 28) and result) then
  begin
    MessageText := 'Number is too long ';
    result := false;
    exit;
  end;

  { check the length }
  if ((Length(StrippedNumber) < 12) or
    ((shiftmask(length(strippednumber)) and themask) = 0)) then
  begin
    messagetext := 'number length incorrect';
    result := false;
    exit;
  end;

  { check the checksum computation }
  if (cardname = 'enroute') then
    performchecksum := false
  else
    performchecksum := true;

  if (performchecksum and (not confirmchecksum(strippednumber))) then
  begin
    messagetext := 'bad checksum';
    result := false;
    exit;
  end;

  { if result is still true, then everything is ok }
  if (result) then
    messagetext := 'number ok: card type: ' + cardname;

  { if the string was zero length, then ok too }
  if (strippednumber = '') then
    result := true;

end;

end.

Nincsenek megjegyzések:

Megjegyzés küldése