185 lines
4.3 KiB
PHP
185 lines
4.3 KiB
PHP
<?php
|
|
/**
|
|
* @brief Credit card object
|
|
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
|
* @copyright (c) Invision Power Services, Inc.
|
|
* @license https://www.invisioncommunity.com/legal/standards/
|
|
* @package Invision Community
|
|
* @subpackage Nexus
|
|
* @since 10 Mar 2014
|
|
*/
|
|
|
|
namespace IPS\nexus;
|
|
|
|
/* To prevent PHP errors (extending class does not exist) revealing path */
|
|
|
|
use DomainException;
|
|
use InvalidArgumentException;
|
|
use function defined;
|
|
use function intval;
|
|
use function strlen;
|
|
|
|
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
|
{
|
|
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Credit card object
|
|
*/
|
|
class CreditCard
|
|
{
|
|
/**
|
|
* @brief Card Number
|
|
*/
|
|
public ?string $number = NULL;
|
|
|
|
/**
|
|
* @brief Last 4 numbers
|
|
*/
|
|
public mixed $lastFour = NULL;
|
|
|
|
/**
|
|
* @brief Type
|
|
*/
|
|
const TYPE_VISA = 'visa';
|
|
const TYPE_MASTERCARD = 'mastercard';
|
|
const TYPE_DISCOVER = 'discover';
|
|
const TYPE_AMERICAN_EXPRESS = 'american_express';
|
|
const TYPE_DINERS_CLUB = 'diners_club';
|
|
const TYPE_JCB = 'jcb';
|
|
const TYPE_PAYPAL = 'paypal';
|
|
const TYPE_VENMO = 'venmo';
|
|
public string $type = "";
|
|
|
|
/**
|
|
* @brief Expire Month
|
|
*/
|
|
public mixed $expMonth = NULL;
|
|
|
|
/**
|
|
* @brief Expire Year
|
|
*/
|
|
public mixed $expYear = NULL;
|
|
|
|
/**
|
|
* @brief Security Code
|
|
*/
|
|
public mixed $ccv = NULL;
|
|
|
|
/**
|
|
* @brief Save
|
|
*/
|
|
public bool $save = FALSE;
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @return void
|
|
*/
|
|
public function __construct() {}
|
|
|
|
/**
|
|
* Create object
|
|
*
|
|
* @param string $cardNumber Card Number
|
|
* @param int|string $expMonth Expire month
|
|
* @param int|string $expYear Expire year
|
|
* @param int|string $ccv Security Code
|
|
* @param bool $save If the card should be saved
|
|
* @return static
|
|
* @throws InvalidArgumentException Card number or expiry date is invalid
|
|
* @throws DomainException Expire date is in the past
|
|
*/
|
|
public static function build(string $cardNumber, int|string $expMonth, int|string $expYear, int|string $ccv, bool $save=FALSE ) : static
|
|
{
|
|
$obj = new static;
|
|
|
|
/* Work out card type */
|
|
$cardNumber = str_replace( array( ' ', '-' ), '', $cardNumber );
|
|
$obj->number = $cardNumber;
|
|
if ( preg_match( '/^4[0-9]{12}(?:[0-9]{3})?$/', $cardNumber ) )
|
|
{
|
|
$obj->type = static::TYPE_VISA;
|
|
}
|
|
elseif ( preg_match( '/^5[1-5][0-9]{14}$/', $cardNumber ) or /* Maestro */ preg_match( '/^(?:5[0678]\d\d|6304|6390|67\d\d)\d{8,15}$/', $cardNumber ) )
|
|
{
|
|
$obj->type = static::TYPE_MASTERCARD;
|
|
}
|
|
elseif ( preg_match( '/^3[47][0-9]{13}$/', $cardNumber ) )
|
|
{
|
|
$obj->type = static::TYPE_AMERICAN_EXPRESS;
|
|
}
|
|
elseif ( preg_match( '/^3(?:0[0-5]|[68][0-9])[0-9]{11}$/', $cardNumber ) )
|
|
{
|
|
$obj->type = static::TYPE_DINERS_CLUB;
|
|
}
|
|
elseif ( preg_match( '/^6(?:011|5[0-9]{2})[0-9]{12}$/', $cardNumber ) )
|
|
{
|
|
$obj->type = static::TYPE_DISCOVER;
|
|
}
|
|
elseif ( preg_match( '/^(?:2131|1800|35\d{3})\d{11}$/', $cardNumber ) )
|
|
{
|
|
$obj->type = static::TYPE_JCB;
|
|
}
|
|
else
|
|
{
|
|
throw new InvalidArgumentException('card_number_invalid');
|
|
}
|
|
$obj->lastFour = mb_substr( $cardNumber, -4 );
|
|
|
|
/* Check it with the Luhn algorithm */
|
|
$sumTable = array(
|
|
array(0,1,2,3,4,5,6,7,8,9),
|
|
array(0,2,4,6,8,1,3,5,7,9)
|
|
);
|
|
$sum = 0;
|
|
$flip = 0;
|
|
$cardNumberLength = strlen( $cardNumber );
|
|
for ($i = $cardNumberLength - 1; $i >= 0; $i--)
|
|
{
|
|
$sum += $sumTable[ $flip++ & 0x1 ][ $cardNumber[ $i ] ];
|
|
}
|
|
if( $sum % 10 !== 0 )
|
|
{
|
|
throw new InvalidArgumentException('card_number_invalid');
|
|
}
|
|
|
|
/* Check the expiry date */
|
|
$expMonth = intval( $expMonth );
|
|
if ( $expMonth < 0 or $expMonth > 12 )
|
|
{
|
|
throw new InvalidArgumentException('card_month_invalid');
|
|
}
|
|
$obj->expMonth = str_pad( $expMonth, 2, '0', STR_PAD_LEFT );
|
|
$obj->expYear = intval( $expYear );
|
|
if ( mktime( 23, 59, 59, $expMonth + 1, 0, $expYear ) < time() )
|
|
{
|
|
throw new DomainException;
|
|
}
|
|
|
|
/* Check the security code */
|
|
$obj->ccv = $ccv;
|
|
if ( $obj->type === static::TYPE_AMERICAN_EXPRESS )
|
|
{
|
|
if ( mb_strlen( $ccv ) !== 4 )
|
|
{
|
|
throw new InvalidArgumentException('ccv_invalid_4');
|
|
}
|
|
}
|
|
elseif ( mb_strlen( $ccv ) !== 3 )
|
|
{
|
|
throw new InvalidArgumentException('ccv_invalid_3');
|
|
}
|
|
|
|
/* Should it be saved? */
|
|
if ( $save )
|
|
{
|
|
$obj->save = $save;
|
|
}
|
|
|
|
|
|
return $obj;
|
|
}
|
|
} |