Author: Fernando Silva
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
1
2 unit Creditc;
3
4 {*****************************************************************************
5
6 Credit Card Number Validator Unit for Delphi
7
8 Version: 1.1
9 Date: December 20, 1996
10
11 This unit is based on the public domain program ccard by Peter Miller.
12 It is released to the public for free of charge use, but the author
13 reserves all rights.
14
15 copyright 1996 by Shawn Wilson Harvell ( shawn@inet.net )
16
17 usage:
18
19 Add this unit to the uses clause of any unit that needs access to the
20 validation function.
21
22 IsValidCreditCardNumber( CardNumber, ReturnMessage ) returns Boolean
23
24 for example, use it in an if statement that Messages user if invalid.
25
26 CardNumber is a string containing the number that you want to validate
27 ReturnMessage is a string where the function can place any messages it
28 may return ( meaning that it will overwrite whatever is in it )
29
30 returns true if valid, false otherwise.
31
32 dashes and space in the input value are taken care of by the function,
33 if other characters are possible, you may wish to remove them as well.
34 The function RemoveChar will take care of this quite easily, simply
35 pass the input string and the char you wish to delete.
36
37 Users are free to modify this unit for their own use, but in
38 distributing you should advise all users of the changes made.
39
40 Use this unit at your own risk, it does not come with any warranties
41 either express or implied. Damages resulting from the use of this
42 unit are the sole responsibility of the user.
43
44 This should work as is for Delphi versions 1 and 2, some slight
45 modifications may be necessary for Turbo Pascal ( mainly due to use to
46 conversion functions from the SysUtils unit ).
47
48 If you do find this useful, have any comments or suggestions, please
49 drop the author an email at shawn@inet.net
50
51 Revision History
52
53 version 1.1 -- December 20, 1996
54 blooper with Discover cards, added their length mask to the "database"
55
56 version 1.0 -- October 26, 1996
57 initial release
58
59 *****************************************************************************}
60
61 interface
62
63 uses SysUtils;
64
65 function IsValidCreditCardNumber(CardNumber: string; var MessageText: string):
66 Boolean;
67
68 implementation
69
70 const
71 CardPrefixes: array[1..19] of string =
72 ('2014', '2149', '300', '301', '302',
73 '303', '304', '305', '34', '36', '37',
74 '38', '4', '51', '52', '53', '54', '55', '6011');
75
76 CardTypes: array[1..19] of string =
77 ('enRoute',
78 'enRoute',
79 'Diner Club/Carte Blanche',
80 'Diner Club/Carte Blanche',
81 'Diner Club/Carte Blanche',
82 'Diner Club/Carte Blanche',
83 'Diner Club/Carte Blanche',
84 'Diner Club/Carte Blanche',
85 'American Express',
86 'Diner Club/Carte Blanche',
87 'American Express',
88 'Diner Club/Carte Blanche',
89 'Visa',
90 'MasterCard',
91 'MasterCard',
92 'MasterCard',
93 'MasterCard',
94 'MasterCard',
95 'Discover');
96
97 function RemoveChar(const Input: string; DeletedChar: Char): string;
98 var
99 Index: Word; { counter variable }
100 begin
101 { all this function does is iterate through string looking for char, if found }
102 { it deletes it }
103 Result := Input;
104 for Index := Length(Result) downto 1 do
105 if Result[Index] = DeletedChar then
106 Delete(Result, Index, 1);
107 end;
108
109 function ShiftMask(Input: Integer): Integer;
110 begin
111 { simply a wrapper for this left bit shift operation }
112 result := (1 shl (Input - 12));
113 end;
114
115 function ConfirmChecksum(CardNumber: string): Boolean;
116 var
117 CheckSum: Integer; { Holds the value of the operation }
118 Flag: Boolean; { used to indicate when ready }
119 Counter: Integer; { index counter }
120 PartNumber: string; { used to extract each digit of number }
121 Number: Integer; { used to convert each digit to integer }
122 begin
123
124 {**************************************************************************
125 This is probably the most confusing part of the code you will see, I know
126 that it is some of the most confusing I have ever seen. Basically, this
127 function is extracting each digit of the number and subjecting it to the
128 checksum formula established by the credit card companies. It works from
129 the end to the front.
130 **************************************************************************}
131
132 { get the starting value for our counter }
133 Counter := Length(CardNumber);
134 CheckSum := 0;
135 PartNumber := '';
136 Number := 0;
137 Flag := false;
138
139 while (Counter >= 1) do
140 begin
141 { get the current digit }
142 PartNumber := Copy(CardNumber, Counter, 1);
143 Number := StrToInt(PartNumber); { convert to integer }
144 if (Flag) then { only do every other digit }
145 begin
146 Number := Number * 2;
147 if (Number >= 10) then
148 Number := Number - 9;
149 end;
150 CheckSum := CheckSum + Number;
151
152 Flag := not (Flag);
153
154 Counter := Counter - 1;
155 end;
156
157 result := ((CheckSum mod 10) = 0);
158 end;
159
160 function GetMask(CardName: string): Integer;
161 begin
162 { the default case }
163 result := 0;
164
165 if (CardName = 'MasterCard') then
166 result := ShiftMask(16);
167 if (CardName = 'Visa') then
168 result := (ShiftMask(13) or ShiftMask(16));
169 if (CardName = 'American Express') then
170 result := ShiftMask(15);
171 if (CardName = 'Diner Club/Carte Blanche') then
172 result := ShiftMask(14);
173 if (CardName = 'Discover') then
174 result := ShiftMask(16);
175
176 end;
177
178 function IsValidCreditCardNumber(CardNumber: string; var MessageText: string):
179 Boolean;
180 var
181 StrippedNumber: string; { used to hold the number bereft of extra chars }
182 Index: Integer; { general purpose counter for loops, etc }
183 TheMask: Integer; { number we will use for the mask }
184 FoundIt: Boolean; { used to indicate when something is found }
185 CardName: string; { stores the name of the type of card }
186 PerformChecksum: Boolean; { the enRoute type of card doesn't get it }
187 begin
188
189 { first, get rid of spaces, dashes }
190 StrippedNumber := RemoveChar(CardNumber, ' ');
191 StrippedNumber := RemoveChar(StrippedNumber, '-');
192
193 { if the string was zero length, then OK too }
194 if (StrippedNumber = '') then
195 begin
196 result := true;
197 exit;
198 end;
199
200 { initialize return variables }
201 MessageText := '';
202 result := true;
203
204 { set our flag variable }
205 FoundIt := false;
206
207 { check for invalid characters right off the bat }
208 for Index := 1 to Length(StrippedNumber) do
209 begin
210 case StrippedNumber[Index] of
211 '0'..'9': FoundIt := FoundIt; { non op in other words }
212 else
213 MessageText := 'Invalid Characters in Input';
214 result := false;
215 exit;
216 end;
217 end;
218
219 { now let's determine what type of card it is }
220 for Index := 1 to 19 do
221 begin
222 if (Pos(CardPrefixes[Index], StrippedNumber) = 1) then
223 begin
224 { we've found the right one }
225 FoundIt := true;
226 CardName := CardTypes[Index];
227 TheMask := GetMask(CardName);
228 end;
229 end;
230
231 { if we didn't find it, indicates things are already ary }
232 if (not FoundIt) then
233 begin
234 CardName := 'Unknown Card Type';
235 TheMask := 0;
236 MessageText := 'Unknown Card Type ';
237 result := false;
238 exit;
239 end;
240
241 { check the length }
242 if ((Length(StrippedNumber) > 28) and result) then
243 begin
244 MessageText := 'Number is too long ';
245 result := false;
246 exit;
247 end;
248
249 { check the length }
250 if ((Length(StrippedNumber) < 12) or
251 ((shiftmask(length(strippednumber)) and themask) = 0)) then
252 begin
253 messagetext := 'number length incorrect';
254 result := false;
255 exit;
256 end;
257
258 { check the checksum computation }
259 if (cardname = 'enroute') then
260 performchecksum := false
261 else
262 performchecksum := true;
263
264 if (performchecksum and (not confirmchecksum(strippednumber))) then
265 begin
266 messagetext := 'bad checksum';
267 result := false;
268 exit;
269 end;
270
271 { if result is still true, then everything is ok }
272 if (result) then
273 messagetext := 'number ok: card type: ' + cardname;
274
275 { if the string was zero length, then ok too }
276 if (strippednumber = '') then
277 result := true;
278
279 end;
280
281 end.
|