Welcome Guest, Not a member yet? Register   Sign In
how to mask credit card number (using regex?)
#1

[eluser]coolfactor[/eluser]
I'm trying to find an elegant way to turn 1234-4567-7890-4433 into xxxx-xxxx-xxxx-4433.

I found this tutorial, but I'm not much of a regex person and don't know how to rewrite that for use in PHP.

Here's the code from that tuturial, in Rails:
Code:
@card_masked = card_masked.sub(/^([0-9]+)([0-9]{4})$/) { '*' * $1.length + $2 }

Which PHP function should I be using?

Your help is appreciated. Thanks!
#2

[eluser]coolfactor[/eluser]
I've managed to separate the two parts of the number. Here's my progress so far:

Code:
$cc = '1234-4344-2994-4394';

$pattern = '/^([0-9-]+)([0-9]{4})$/';

$matches = array();

preg_match($pattern, $cc, $matches);

The result is:
Code:
array(3)
{
    [0]=> string(19) "1234-4344-2994-4394"
    [1]=> string(15) "1234-4344-2994-"
    [2]=> string(4) "4394"
}

Now, how do I use preg_replace() instead to mask element [1]?
#3

[eluser]coolfactor[/eluser]
Got it. Here's the completed code. It accepts hyphen or space-delimited numbers, or even numbers without any delimiters.

Code:
function mask_cc($cc, $mask_char = 'X')
{
    $pattern = '/^([0-9- ]+)([0-9]{4})$/';
    $matches = array();
    preg_match($pattern, $cc, $matches);
    return preg_replace('([0-9])', 'X', $matches[1]).$matches[2];
}

$cc = '1234-4344-2994-4394';

echo mask_cc($cc, '*');

Echos: ****-****-****-4394
#4

[eluser]jupiter909[/eluser]
Hmmm the long way round. You'd much rather be using regex for things such as verifying credit-card numbers/phone numbers/postal codes.

To change 1234-4567-7890-4433 into xxxx-xxxx-xxxx-4433. is as easy as
Code:
$last_four_digits = substr("$credit_card_number", -4);
$masked = "xxxx-xxxx-xxxx-".$last_four_digits;

Avoid using regex when not needed.
#5

[eluser]coolfactor[/eluser]
Why? The point was to mask the *actual* entry, not assume it's in a certain format.

234876512483211
************2113

2348-7651-2483-2113
****-****-****-2113

etc.
#6

[eluser]Pygon[/eluser]
A fix note to coolfactors code:
Code:
return preg_replace('([0-9])', 'X', $matches[1]).$matches[2];

should be:

return preg_replace('([0-9])', $mask_char, $matches[1]).$matches[2]

However -- I would do:
Code:
function mask_cc($cc, $mask_char='X')
{
    trim($cc);
    $matches = str_split($cc,strlen($cc)-4);
    return preg_replace('([0-9])', $mask_char, $matches[1]).$matches[2];
}
instead. That assumes that you are verifying that the CC is valid as far as four sets of four number, seperated by three hyphens, spaces, or not seperated.

Honestly, I would wonder why you feel it important to return information in the same form it is input? I would have no interest in making sure that the version they see matches the format they typed in... and I expect that, when I make purchases, my card may be entered with or without spaces, or with hyphens, but that it will be displayed as ************XXXX <card type> as a result. I see no reason to adopt user-input formating as to how my system handles and displays billing information.

My very personal version would be:
Code:
function mask_cc($cc, $mask_char='X')
{
    $cc = preg_replace('([^0-9])', '', $cc);
    $last = substr( $cc,-4);
    return str_pad($last,16,$mask_char,STR_PAD_LEFT);
}
Which is by far the fastest of all three and enforces standards.
#7

[eluser]untermensch[/eluser]
Any purely regex solution to the above problem would not look very elegant. A rather simple way to achieve the desired result would be:

Code:
function mask_cc($cc, $mask_char='X')
{
    return preg_replace("/[^- ]/", $mask_char, substr($cc,0,-4)) . substr($cc,-4);
}

Which preserves the format (spaces & dashes) and leaves the last 4 digits unaltered, as per the request. Not sure why everybody is over-engineering the solutions to this problem. There are more difficult things to be worrying about!
#8

[eluser]llbbl[/eluser]
its a regex deathmatch ... ok go!

nice job thou Smile
#9

[eluser]Pygon[/eluser]
I don't think it has to do with "over-engineering". Mine is a concern for speed and enforcing standards, while allowing user input to be anything they want while still working. Should your function encounter a trailing hyphen, it would not work properly.

"Simplicity", in the form of one-line functions, doesn't necessarily mean it is the best solution. Regex is great but takes it's toll on the CPU. String manipulation uses far less CPU, and is often the same speed or faster. My function is limited in scope as to just masking any card that is entered (provided there are atleast 4 numbers). A more functional solution would be a seperate function which strips all formatting, allowing one variable (containing the card number after being stripped) to be used for multiple things like:
simple form verification - strlen(16)
pre-auth - no need to strip formatting, again.
billing - same as above
receipt(masking) - same as above

One regex versus many is always better. Why waste more CPU cycles maintaining a user's input rather than spending them on something of use.

Our goal, as web developers, is to unrestrict users (by not forcing them to follow standards), while enforcing standards internally for speed, maintenance and operations. Approaching anything with "just do something that works, there are more important things" breeds future problems--always. Have a read through WorseThanFailure if you think otherwise.
#10

[eluser]untermensch[/eluser]
[quote author="Pygon" date="1194044896"]Mine is a concern for speed and enforcing standards, while allowing user input to be anything they want while still working. Should your function encounter a trailing hyphen, it would not work properly.[/quote]
Mate, have you even tried running your one above? The preg_replace is superfluos, and it strips out the hyphens, and assumes all CC numbers have 16 digits (they vary between 14 and 16).

Besides which, nobody asked about a CC number *validator*. It is assumed validation has already been done. The request was only to mask out all but the last 4 digits - so the trailing hyphen point is entirely moot (although your solution suffers from the same "problem").

[quote author="Pygon" date="1194044896"]"Simplicity", in the form of one-line functions, doesn't necessarily mean it is the best solution.[/quote]
Simplicity is readability.

[quote author="Pygon" date="1194044896"]Regex is great but takes it's toll on the CPU.[/quote]
Which makes me wonder why you have a redundant preg_replace Smile

[quote author="Pygon" date="1194044896"]My function is limited in scope as to just masking any card that is entered (provided there are atleast 4 numbers)[/quote]

Which is exactly what the original poster asked for...




Theme © iAndrew 2016 - Forum software by © MyBB