views:

3383

answers:

19

Some fancy websites show an error dialog when it is detected that an untrained shopper has entered a credit/debit card number as it is printed on their card with spaces. Is it possible in some way to write a Java web app that handles these numbers with spaces as if they were correct?

A: 

Sure. Compress out the spaces. There are probably a zillion ways to do it; I'd be tempted to use String.split() to break it up on the spaces, then concatente the four strings resulting. Or use StringBuffer.indexOf() and .delteCharAt().

...or as Cletus said, use replaceAll().

Charlie Martin
+1  A: 

There are a number of options, but the most logical seems to be to just do a simple string replace to replace all the spaces with a closed character i.e. ''. This will reduce the credit card string to just one long string of numbers..then just process away

Jamie Lewis
+66  A: 

My view is that any Web app that rejects a credit card number with spaces isn't doing its job. When you receive a credit card number, it's easy enough to do:

String ccNumber = ccNumber.replaceAll("[\\s-]+", "");

to remove spaces and dashes (some use those too). Then validate the result. You'll simply annoy your users if you force them to remove spaces you could just as easily do.

As for how to validate, well that depends on a lot of things, such as which Web framework you're using and what validation options you've chosen. Struts 1 for example might or might not use Apache Commons Validator whereas Spring MVC will (probably) use Spring validation and so on. So I can't tell you exactly how to validate but I can tell you what to validate.

The first thing is that a CC number with spaces should not be rejected. Most people will find:

4123 0987 8876 2939

much easier to read than:

4123098788762939

which is really important if the user misses or mistypes a digit and needs to find why his or her credit card number failed validation. The replaceAll() at the top of this post covers this situation.

The second thing is that you display the credit card number (even when some of the digits are replaced with X for security reasons) in the correct way. I suggest you read through Anatomy of Credit Card Numbers.

That page gives you the rules for the number of digits and the valid prefixes. A robust Web application will implement these so you can tell if a credit card number is invalid before you try and use it. It can take up to 30 seconds (or possibly more) to submit credit card details to a payment gateway so you shouldn't do it until you are sure as you can be that the payment will be accepted. To do otherwise is to provide a really bad user experience. There is every chance the user will give up if it fails 1-2 times rather than wait.

As for displaying them, that depends on the # of digits:

  • 16: 4 groups of 4 separated by a space;
  • 15: like an American Express card ie 4-6-5 with a space between each group;
  • 14: like a Diners Club card ie 4-6-4 with a space between each group;
  • 13: Never seen 13 but 4-5-4 or 4-4-5 or 5-4-4 (or possibly 3-3-3-4) springs to mind.

The credit card number should be verified according to the checksum algorithm mentioned in the page before submitting for processing as part of a standard validation routine. That page has a Java implementation of that routine.

Every website that accepts credit card payment should be doing all of the above as an absolute minimum or you're simply throwing away business as a percentage of your users get frustrated.

So the short version is two simple rules:

  1. Be as forgiving as possible with user input; and
  2. Do absolutely everything possible to validate credit card details prior to submission.
cletus
Web sites with "no spaces please" are a red alert in my book. What else are they doing wrong? How can you trust them with any personal information?
S.Lott
Maybe it would be better to use a regex which removed any character that isn't a number. But yes, I agree that any website which doesn't do something this basic is not doing its job properly!
Phill Sacre
@Phill Sacre, I disagree. Something that isn't a number is invalid, and should be treated as such. Other than spaces or dashes, I mean. Letters are a sign that the user mistyped.
Matt Olenik
"Additionally you should also verify the CVC" <-- the check digit and the CVC are very different things. The design of the CVC is such that you cannot verify it without submitting it to the issuing bank. The check digit, however, has a well-known algorithm behind it.
bdonlan
Actually, most website that don't allow spaces are user-correcting since they set the CC field width to 16 so you can't enter a MC or Visa CC# with spaces.
jmucchiello
I hate it when websites give me 4 input boxes, or when they don't accept my input... I'd have 2 boxes, a big one (CC Number) and a small one (CVV), both would have s/\D//g; to filter them to digits, then check validity and number of digits. I'd automatically detect the CC type since they have fixed prefixes.
Osama ALASSIRY
In the old days, 13-digit card numbers were used. They were going the way of the dodo when I was coding a web app to handle credit card numbers back in 1998. The approved format was 4-3-3-3.
Jonathan Leffler
A very helpful answer to a not very helpful question. Apparently.
Tom Hawtin - tackline
@Jonathan: I actually came across my 13-digit visa just a few months ago, with an expiration date of 1989 :-)
Eric J.
+5  A: 

Your question seems strange but I would think that it should be as easy as running the credit card numbers entered by users through a validation function which would first of all remove all white spaces.

This is rather trivial in any modern language with or without using regex.

+7  A: 

Unfortunately not, which why instead those fancy web sites need to show an error dialog to the untrained shopper: to force the shopper to re-enter their number, in the format that the machine prefers.

Why, if only a machine could do "data processing", so that the machine itself could change the data format! Or, if only if there were no such thing as an "untrained" shopper! Alas!

ChrisW
Chris, clearly it's *technologically* possible either in JavaScript on the client side or Java on the server side. Do you mean it is *legally* impossible to modify the input? That any transformation of what the user entered is illegal?
dj_segfault
I was being tongue-in-cheek ... facetious ... I was, in fact, lying ... but I'm sure that the OP, who asked the so-called 'question', would know that.
ChrisW
+11  A: 

Websites that force you to enter credit card numbers (and similar things) in a specific format - seriously annoy me.

Those people are inconveniencing their customers simply because they (the developers) are lazy. There is no reason not to except things like credit card numbers, phone numbers, etc in whatever format they are provided. The only limitation is what is REQUIRED to understand how to interpret the value.

You shouldn't care whether I enter 5555-4444-3333-2222 or 5555444433332222, just strip the dashes out if you don't like them - same with spaces. And with phone numbers, unless you are going to be auto-dialing the number, you probably don't even care what format its in so don't annoy your users unless you have to.

kenj0418
+18  A: 

I would go as far as stripping out all non-numeric characters then checking that the length is valid before running the user input through real validation like Luhn's algorithm.

String ccNumber = input.replaceAll("\\D", "");

strips out all the non-digits from String input.

Bill the Lizard
I think there is a case for CC numbers containing alphabetic characters to fail validation
Harry Lime
what is that case? And if there is a case, the software should reject characters outright rather then allow the user to enter them only to slap their hand later.
Bryan Oakley
Harry Lime
Remember, the goal of software is to serve the user, not the other way around.
Bryan Oakley
@Harry: I agree that a case could be made. It's very unlikely that a user means to type in 1234a5678b9012c3456. Most people would consider that garbage, even though there might be a valid CC number embedded in there. Stripping out all non-numerics allows things like 1234|5678 |9012|3456 to pass, though, where only allowing certain delimiters like space and dash would fail this example. I'd rather allow anything containing the right digits in the right order than make the user remember how I like it typed in.
Bill the Lizard
0l23 4567 89O1 2345? Mind you, entering an IBAN confused the hell out of me.
Tom Hawtin - tackline
@Tom: Sorry about that. I'm not sure that's a valid IBAN, but I just typed in the first 16 digits I thought of. Next example was 3141 5926 5358 9793.
Bill the Lizard
Bill: So my example was typing whatdoyoucallem (homographs??) letters that look like digits. Probably a more popular thing to do in the 80's. IIRC, IBANs can be presented with spurious characters at the front which aren't (according to some webapps!) part of the IBAN. I copied and pasted, got confused by confusing webapp. Deleted some characters at the front and it still didn't work. Of course, being overlong the last digits were truncated. That raises questions of usability that are actually in a grey area, IMO. Get similar issues copying and pasting my phone number.
Tom Hawtin - tackline
@Tom: You totally confused me before. I didn't even notice the letters in your example, so point taken. :) If you strip those characters out the number will be too short and report an error that the user has trouble seeing. The server-side error checking might need to be smart enough to report that this could be the case.
Bill the Lizard
+9  A: 

Unfortunately, no. Java just cannot handle these requirements, since there is so much overhead involved in emulating the Java Virtual Machine on x86 chips, leaving no room for useful constructs like Perl's regular expressions, which can do it thusly:

$input =~ s/\D//g;

Java made an attempt at adding regular expressions a few years back, but they only ran on PowerPC chips, which are no longer used. The problem was that all regular expressions had to be contained as Strings instead of being a first class language construct, and thus doubling backslashes was required, but as everyone knows backslashes mean something different on the primary operating system for the x86 architecture.

My advice is to upgrade to Perl. Scheme is also known to be able to handle this situation as well as give a tremendous advantage over your competition, but Scheme runs only on LISP machines.

skiphoppy
funny, but not funny enough to get my vote.
Bryan Oakley
funny enough to get mine, but only because I have a good coffee now :)
Vladimir Dyuzhev
Funny enough for me to cancel someone's downvote out of perversity.
Jesse Millikan
the scheme comment at the end tipped it over the edge for me :)
Martin DeMello
needs more Erlang
Karl the Pagan
+4  A: 

Easy.

  • Your input space is a list of characters from some character set containing all characters.
  • Your output space is a list of characters from some character set containing only numbers.

To solve this problem, we create an intermediate space containing only the numbers 0 to 9. We can create a new enum for this finite set. We'll call this our finger space, since it oddly contains the same number of members as we do fingers.

We then write two functions.

  1. Convert input space to finger space
  2. Convert finger space to output space

As we reduce the input space to the finger space, we just drop any character not found in the finger space. Converting from finger space to output space is even easier. We just find the same number in the output space.

The trick is for this to work with all character sets. I haven't figured out how to determine if a certain character matches a member in my finger set. Maybe I should post it as a question.

Pyrolistical
or.... you could just strip out everything that isn't a digit?
Egwor
whoa whoa whoa, you are skipping steps! Just because the input space and output space are the same, it doesn't mean you should shortcut the process. You want everything to be well defined, hence the clear unambigious finger space.
Pyrolistical
+1  A: 

Advertisement on this website... For just $49.95 you can have a new special keyboard compatible with this online store. Click here to add the new keyboard in your cart and checkout. When checking out please enter your credit card number in the designated field. Please do not enter the spaces between the numbers as our store doesn't know how to deal with spaces between the numbers.

Jeffrey Hines
+1  A: 

Tom,

The problem is solved technically, let's talk about it theoretically.

There are two schools of thought here. I do not think it is an acceptable answer to say "if the user can't figure it out it's their problem."

  1. Be firm about your user input and only accept credit card numbers that are well-formed. This requires keeping the user on the page until they get everything right.
  2. Be more lenient by assuming their intentions and adjusting their input for them (just be sure to give them a confirmation screen to verify the new input).

In my opinion, #2 is the way to go, you can use regular expressions (as stated above) to pull all spaces, special characters, etc. out of the cc# field and keep the user from having to enter their information again.

Either way you should inform the user of the proper input form (i.e. xxxx-xx-xxxx)

as a rule of thumb, I tend to appreciate sites that are more elegant about the way they handle user input.

For more tips on regular expressions check out regular-expressions . info

Good luck,

-Robert

Robert Greiner
+1  A: 

I'm going to assume this is a real question even though it looks like some sort of troll or joke.

You should model your interface so that the user instinctively performs the input in a controlled manner. Simply put, ask them for the kind of card first, and then on the input form format the input to match the card. For example, assuming a 16 digit card like Visa or Mastercard, display 4 input boxes separated by spaces or dashes that limit input to 4 characters each and automatically move to the next box in the series after the user types the fourth digit.

It should look something like the following on the page:

Card Number:
[1234] - [1234] - [1234] - [1234]

or

Card Number:
[1234] - [123456] - [12345]

Orclev
You know, an actual credit card company itself did that too me the other week. I mistyped (possibly missed a digit?) and had an ordeal correcting myself because of these boxes and focus flying about.
Tom Hawtin - tackline
Yeah, you need to be smart about shifting the focus, if you do a dumb implementation that just shifts focus on key input where the string length is equal to 4 then it becomes a real PITA to delete a number and replace it.
Orclev
It's certainly easy to produce a UI that causes awkwardness in some unforeseen situations. Particularly in a "hostile environment".
Tom Hawtin - tackline
A: 

you could use javascript validation by using the onkeypress event to check if the last character is valid and if not just remove it and maybe even flash up a message saying that an invalid character was entered. This way invalid numbers are never entered. It could also automatically enter a spacer character (space or -) in the format you want.

zonkflut
A: 

I wrote this pair of Perl functions in a shop where only Visa was allowed (...at Visa, actually...) back in 1998.

sub mod10_checkdigit
{
    my($acct) = @_;
    die "invalid account number in BRPS::mod10_checkdigit"
        unless $acct =~ m%^\d+$%;
    my(@digits) = split //, $acct;
    my($len) = scalar(@digits);
    print "# ($len) @digits\n" if ($ENV{PERL_BRPS_DEBUG});
    my($i, $sum, $chk);
    my($mul) = (($len % 2) == 1) ? 1 : 2;
    $len--;
    for ($i = 0; $i < $len; $i++)
    {
        my($val) = $mul * $digits[$i];
        # Note that we need the digital root of the value, but $val is not
        # greater than 18 (because $digits[$i] <= 9 and $mul <= 2).
        $val -= 9 if ($val >= 10);
        $sum += $val;
        print "# $i: $digits[$i] * $mul => $val => $sum\n" if ($ENV{PERL_BRPS_DEBUG});
        $mul = 3 - $mul;
    }
    $chk = 10 - ($sum % 10);
    $chk = 0 if ($chk == 10);
    return $chk;
}

sub validate_account
{
    my($acct) = @_;
    # Strip leading and trailing blanks
    $acct =~ s/^\s*(\S.*\S)\s*$/$1/;
    my($clean) = $acct;
    # Check that account number is string of digits, blanks and dashes
    return undef, "account number is not a sequence of digits, blanks and dashes"
        unless $acct =~ m/^[- \d]+$/;
    return undef, "account number is not a Visa account number"
        unless $acct =~ m/^4/;
    # Remove non-digits
    $clean =~ s/\D//g;
    return undef, "account number is neither 13 nor 16 digits"
        unless length($clean) == 16 || length($clean) == 13;
    # Punctuators must be reasonably consistent!
    return undef, "invalid punctuation pattern"
        unless ($acct =~ m/^\d{16}$/o or $acct =~ m/^\d{13}$/o or
                $acct =~ m/^\d{4}[- ]\d{4}[- ]\d{4}[- ]\d{4}$/o or
                $acct =~ m/^\d{4}[- ]\d{3}[- ]\d{3}[- ]\d{3}$/o);
    # Determine check digit
    my($chk) = mod10_checkdigit($clean);
    return undef, "check digit on account number is incorrect"
        unless $clean =~ m/$chk$/;
    return $clean, "ok";
}

The allow plausible credit card numbers through. It wouldn't be hard to generalize to handle Mastercard, Discover, American Express too.

Rant

I do not like web sites that insist on me entering data in their internal format. Dammit - store the number as a large integer and send it as a pure digit string; that's fine for computers. But do let me enter recognizable human legible formats - even re-present the data in the human-legible format. There is far, far, far too much laziness in web sites that handle credit card numbers.

Jonathan Leffler
A: 

If you mean javascript, you could go with the "advance to next input" method:

Here's the HTML:

<form id="ccform" action="cc_submit.php" method="post">
    <fieldset id="ccnumber">
        <input id="firstset" type="text" maxlength="4" />
        <input id="secondset" type="text" maxlength="4" />
        <input id="thirdset" type="text" maxlength="4" />
        <input id="fourthset" type="text" maxlength="4" />
    </fieldset>
</form>

And here's the JS:

var ccfields;

function moveToNext(e) {
 var field = e.currentTarget;
 var chars = field.value.length;
 var setnumb = Number(field.id.substr(3,1)) - 1;
 if(chars >= 4 && setnumb < 3) {
  ccfields[setnumb + 1].focus();
 }
}

window.onload = function() {
 ccfields = document.getElementById("ccnumber").getElementsByTagName("input");
 for (var i = 0; i < ccfields.length; i++) {
  ccfields[i].onkeyup = moveToNext;
 }
};

Of course, you will want to add a function that checks for non-numbers and a function for taking the four fields and merging them into one string to pass back to the form. It also isn't a bad idea to use a js library like Jquery to ensure that events are handled the same way and simplify traversing through the inputs so you can use attributes like "name" without any confusion.

But generally, if people see 4 fields, it makes it easier to type in their number, and for those visitors who think "ah nuts, I have to use my mouse for each number" they are (or at least I am) pleased that the page is smart enough to know to move to the next field.

Anthony
Is that likely to cause problems if I do something like left arrow, shift tab or perhaps even backspace? Also, one of my cards has 19 digits.
Tom Hawtin - tackline
Please no - this is a horrible idea. Sites that try to be 'helpful' and move to the next field when they think you've completed input in a field like this do two things. One, they make people who are tabbing to the next field because they know they are done skip fields and have to go back. And two, they make it darn near impossible to correct a field if you made a typo.
kenj0418
A: 

Learn regular expressions.

akway
Good advice. But surely this is a problem that could in theory be solved by the unwashed masses.
Tom Hawtin - tackline
+1  A: 

only one person seems to have mentioned the Luhn or mod 10 algorithm

http://en.wikipedia.org/wiki/Luhn_algorithm

malcolmm
Does it need to be mentioned more than once?
Tom Hawtin - tackline
A: 

Solution 1: How about just put in 4 text boxes that can take 4 digit numbers. Like how the license keys for software's are entered. You can trigger the boxes to change to the next in line upon entering a space character or the tab character.

Solution 2: Use regular expressions as mentioned in one of the comments described above. Problem being, you will be susceptible to injection attacks if its a web application.

Ritesh M Nayak
A: 

JavaTM is great for server-side programming, while javascript is may be useful on the client side; for example during validation.

A valid credit card number varies in length between 12 (e.g. Maestro) and 19 (e.g. Solo, Switch). The client-side javascript could find out whether the card number is valid (only digits (with dashes or whitespace), suits the issuer authority, checksum's ok, ...) and perform a 1:1 mapping from a 'human-readable' (e.g.

American Express    3400 0100 2000 009

) to a internal representation, e.g.

<input ... id="ccid" value="340001002000009">
<input ... id="ccissuer" value="AMEX">

Once the credit card information validation has passed the validation during input, the values can be transparently converted into the internal form on-submit.

An example of a plain javascript function that does some checks on that matter may be found on the net, e.g. here http://www.braemoor.co.uk/software/creditcard.shtml.

So much for a description of an answer.

The MYYN