forked from jonathanwkelly/PHP-Credit-Card-Validation-Script
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcreditcards.class.php
436 lines (353 loc) · 13.3 KB
/
creditcards.class.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
<?php
/**
*
* CREDIT CARD VALIDATION SCRIPT
*
* @author Jonathan W. Kelly <[email protected]>
* @version 1.0
*
*
* ABOUT
* --------------------------------------------------------------------
* This class is written to be used as a credit card utility, allowing
* you to do validate the format of certain card types.
*
* Much of the information used to form the logic of this
* class was sourced from Michael Gilleland's Java class,
* which can be found at: http://www.merriampark.com/anatomycc.htm
* See his page(s) for his sources, and the RESOURCES section below
* for more on my resources.
*
*
* SOURCE
* --------------------------------------------------------------------
* https://github.com/jonathanwkelly/PHP-Credit-Card-Validation-Script
*
* The github repo also contains a GUI test interface.
*
*
* TERMS
* --------------------------------------------------------------------
* IIN: Issuer Identification Number
*
* MII: Major Industry Identifier
*
* Check Digit: The last digit of a card number, that is used in the
* Luhn Algorithm (see below) to determine if the number is a
* valid format.
*
* Luhn Algorithm: The algorithm used my most major card issuers to
* determine if a card format is valid.
*
*
* RESOURCES
* --------------------------------------------------------------------
* IIN and MII: http://en.wikipedia.org/wiki/Bank_card_number
*
* Luhn Algorithm: http://en.wikipedia.org/wiki/Luhn_algorithm
*
* ISO/IEC 7812 Standards: http://en.wikipedia.org/wiki/ISO/IEC_7812
*
*
* CUSTOMIZATION
* --------------------------------------------------------------------
* To make this class work for your application, you should
* review and make appropriate adjustments to:
* var arrCardTypes
* var arrAcceptedMII
*
*
* RISK
* --------------------------------------------------------------------
* This script has been thoroughly vetted with dozens of real VISA, AMEX
* and MC numbers (I didn't have accessibility to any DISCOVER cards),
* with legitimate responses. However, you too should test before
* implementing into your application. The source files are available
* at my github (see SOURCE section above). You should also review the
* IIN values at the RESOURCES listed above to ensure they are still
* accurate at the time of implementation.
*
*
* LIABILITY
* --------------------------------------------------------------------
* You are solely responsible for any loss or security issues caused by
* any implementation of this script, either as a whole or any part(s).
*
**/
class CreditCardValidator {
/**
* Elements include:
* 'status' => 'valid' / 'invalid'
* 'type' => 'visa'
* 'substring' => '***1234'
* 'reason' => 'iin' / 'mii' / 'format' / 'cardtype' / 'algorithm'
* @var array arrCardInfo
*/
private $arrCardInfo = array(
'status' => null,
'type' => null,
'substring' => null,
'reason' => null
);
/**
* Contains the accepted card types that this class knows about / uses
* Elements include:
* 'name' => string,
* 'active' => true / false
* 'iinrange' => n-n / n,n / n-n,n,n,n-n
* 'length' => n
* @var array arrCardTypes
*/
private $arrCardTypes = array(
'amex' => array(
'name' => 'American Express',
'active' => true,
'iinrange' => '34,37',
'length' => 15
),
'discover' => array(
'name' => 'Discover',
'active' => true,
'iinrange' => '6011,622126-622925,644-649,65',
'length' => 16
),
'mastercard' => array(
'name' => 'MasterCard',
'active' => true,
'iinrange' => '51-55',
'length' => 16
),
'visa' => array(
'name' => 'VISA',
'active' => true,
'iinrange' => '4',
'length' => 16
)
);
/**
* Holds the acceptable MII types that will be considered by this
* class as acceptable. See the MII resource listed at the top of this
* document for info on these values.
* @var array arrAcceptedMII
*/
private $arrAcceptedMII = array(3, 4, 5, 6);
/**
* Will determine if the card number passed is legitimately
* formatted. If the strCardType param is passed, it will only
* check against that format. Otherwise, it will check against
* all possible card formats. In addition to returning a straight
* boolean true / false, this method will also set more details
* about the card to the classes arrCardInfo property.
*
* If the validation fails, one of the following reasons will be
* assigned to the arrCardInfo['reason'] property. Possible values
* are listed below, with the explanation of each:
* iin The IIN was not valid, according to CheckIIN()
* mii The IIN was not valid, according to CheckMII()
* format The format (e.g. length or characters) was invalid
* cardtype The card type argument was invalid or not accepted
* algorithm The format was found to be invalid, according to CheckLuhn()
*
* @param string strCardNumber The raw number
* @param string optional strCardType Pass in to validate against a certain card type / format
* @return boolean True if is valid, False otherwise
* @access public
*/
public function Validate($strCardNumber=null, $strCardType=null) {
// We need a valid string passed in
if($strCardNumber === null) {
$this->arrCardInfo['failure'] = 'format';
$this->arrCardInfo['status'] = 'invalid';
return false;
}
// We either need no card type passed, or a valid card type passed
if(($strCardType !== null) && !in_array($strCardType, $this->arrCardTypes)) {
$this->arrCardInfo['failure'] = 'cardtype';
$this->arrCardInfo['status'] = 'invalid';
return false;
}
// Check the MII
if(!$this->CheckMII($strCardNumber)) {
$this->arrCardInfo['failure'] = 'mii';
$this->arrCardInfo['status'] = 'invalid';
return false;
}
// Check the IIN
if(!$this->CheckIIN($strCardNumber)) {
$this->arrCardInfo['failure'] = 'iin';
$this->arrCardInfo['status'] = 'invalid';
return false;
}
// Check the Luhn Algorithm
if(!$this->CheckLuhn($strCardNumber)) {
$this->arrCardInfo['failure'] = 'algorithm';
$this->arrCardInfo['status'] = 'invalid';
return false;
}
// If we get here, it's valid and we go ahead and set the arrCardInfo details
$this->arrCardInfo['status'] = 'valid';
$this->arrCardInfo['substring'] = $this->GetCardSubstring($strCardNumber);
return true;
} # END METHOD Validate()
/**
* Gets rid of any non numeric characters in the param string
* @param string strCardNumber The raw card number
* @return string
* @access private
*/
private function CleanCardNumber($strCardNumber=null) {
// Get rid of any dashes, spaces or alpha chars
return preg_replace('/[^0-9]/', '', $strCardNumber);
} # END METHOD CleanCardNumber()
/**
* Will take the raw card number and return the ***1234 format
* @param string strCardNumber The raw card number
* @return string
* @access private
*/
private function GetCardSubstring($strCardNumber=null) {
// If we got passed the already truncated / short form card number,
// then just send that back. But before we do, make sure we're not
// sending back the whole number!
if(strstr($strCardNumber, '*') && (substr($strCardNumber) < 10)) return $strCardNumber;
// Clean the card number before we substring it
$strCardNumber = $this->CleanCardNumber($strCardNumber);
// Return the truncated card number, or just an empty string if the param was null
return $strCardNumber ? '***'.substr($strCardNumber, (strlen($strCardNumber) - 4), 4) : '';
} # END METHOD GetCardSubstring()
/**
* Will check the first digit of a card number, or the "MII" -
* Major Industry Identifier. Below are the options. The only
* ones that will cause this method to return a True response
* are those configured in the arrAcceptedMII property.
*
* MII Digit Value Issuer Category
* 0 : ISO/TC 68 and other industry assignments
* 1 : Airlines
* 2 : Airlines and other industry assignments
* 3 : Travel and entertainment
* 4 : Banking and financial
* 5 : Banking and financial
* 6 : Merchandizing and banking
* 7 : Petroleum
* 8 : Telecommunications and other industry assignments
* 9 : National assignment
*
* @param string strCardNumber The card number to be checked
* @return boolean True if it is an acceptable MII type
* @access private
*/
private function CheckMII($strCardNumber=null) {
// Clean the card number before we eval it
$strCardNumber = $this->CleanCardNumber($strCardNumber);
// Gotta have anumber to eval!
if(!$strCardNumber) return false;
// Get the first digit and see if it is in the whitelist
$intFirstDigit = (int) substr($strCardNumber, 0, 1);
if(!in_array($intFirstDigit, $this->arrAcceptedMII)) return false;
// If we get here, it is legit so return true
return true;
} # END METHOD CheckMII()
/**
* Will check the card number format based on the mod 10 Luhn algorithm.
* See the RESOURCES section at the top of the document for more details.
* @param string strCardNumber The raw number to check
* @return boolean True if it checks out
* @access private
*/
private function CheckLuhn($strCardNumber=null) {
// Clean the number passed in
$strCardNumber = (string) $this->CleanCardNumber($strCardNumber);
// First, get the check digit (the last digit)
$strCheckDigit = substr($strCardNumber, (strlen($strCardNumber) - 1), 1);
// Now reverse the card number, double every second values (and combine their digits), and tally them all
$strCardNumberReverse = strrev($strCardNumber);
$intTotal = 0;
for($i = 1; $i <= strlen($strCardNumberReverse); $i++) {
// Double every other number
$intVal = (int) ($i % 2) ? $strCardNumberReverse[$i-1] : ($strCardNumberReverse[$i-1] * 2);
// Sum any double digits
if($intVal > 9) {
$strVal = (string) $intVal;
$intVal = (int) ($strVal[0] + $strVal[1]);
}
// Throw it in the array to be tallied
$intTotal += $intVal;
}
// Now check to see if our sum mod 10 == 0
return (($intTotal % 10) == 0) ? true : false;
} # END METHOD CheckLuhn()
/**
* Will check to see if the first six digits (including the MII) is
* within the valid range(s) for the types of cards we are accepting.
* @param string strCardNumber The card number to be checked
* @return boolean True if it is an acceptable IIN
* @access private
*/
private function CheckIIN($strCardNumber=null) {
// Clean the number passed in
$strCardNumber = $this->CleanCardNumber($strCardNumber);
// Gotta have a number passed in
if(!$strCardNumber) return false;
// This will hold any matches. Hopefully we'll only have one!
$arrCardTypePossibilities = array();
// Loop through all the accepted card types, and check our num against them
foreach($this->arrCardTypes as $strShortName => $arrCardType) {
// Only if it's active!
if($arrCardType['active'] === true) {
// First, do the easy job of checking the length
$strLen = strlen($strCardNumber);
if($strLen == $arrCardType['length']) {
// Now, unpack the IINs and compare against them
// This will get all the "range sets", which are the comma delimited items
$arrRangeSets = explode(',', $arrCardType['iinrange']);
foreach($arrRangeSets as $strRangeSetItem) {
// Get any ranges that are hyphen delimited items, denoting ranges
$arrStrRanges = explode('-', $strRangeSetItem);
// arrStrRanges should contain either an array (if it was a hyphenated range)
// or a single number. If it's an array, we need to check overy value in the range.
// Check every value in the range
if(count($arrStrRanges) > 1) {
for($i = $arrStrRanges[0]; $i <= $arrStrRanges[1]; $i++) {
if(
(strpos((string) $strCardNumber, (string) $i) === 0) &&
!in_array($strShortName, $arrCardTypePossibilities)
) $arrCardTypePossibilities[] = $strShortName;
}
// Check against one value
} else {
if(
(strpos((string) $strCardNumber, (string) trim($arrStrRanges[0])) === 0) &&
!in_array($strShortName, $arrCardTypePossibilities)
) $arrCardTypePossibilities[] = $strShortName;
}
} # end range sets foreach loop
} # end length conditional
} # end active conditional
} # end card types loop
// Now assign the possible card type values to the arrCardInfo property
$this->arrCardInfo['type'] = implode('|', $arrCardTypePossibilities);
// Return true if we found at least one possibile type
return count($arrCardTypePossibilities) ? true : false;
} # END METHOD CheckIIN()
/**
* Simply returns the otherwise inaccessible protected
* property arrCardInfo
* @param void
* @return array The arrCardInfo property
* @access public
*/
public function GetCardInfo() {
return $this->arrCardInfo;
} # END METHOD GetCardInfo()
/**
* Will return the literal / friendly name value for the card
* @param string strCardShortName E.g. "amex" or "visa"
* @return string
* @access public
*/
public function GetCardName($strCardShortName=null) {
return isset($this->arrCardTypes[$strCardShortName]['name']) ? $this->arrCardTypes[$strCardShortName]['name'] : '';
} # END METHOD GetCardName()
} # END CLASS CreditCardValidator()
?>