# Checking Credit Cards (#122)

**URL:** <https://www.ruby-forum.com/t/checking-credit-cards-122/93097>\
**Category:** Ruby\
**Created:** [April 27, 2007, 2:00pm UTC](https://www.ruby-forum.com/t/checking-credit-cards-122/93097 "2007-04-27T14:00:30Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [April 27, 2007, 2:00pm UTC](https://www.ruby-forum.com/t/checking-credit-cards-122/93097/1 "2007-04-27T14:00:30Z")

</div>

The three rules of Ruby Q.:

1. Please do not post any solutions or spoiler discussion for this quiz  
until  
48 hours have passed from the time on this message.

2. Support Ruby Q. by submitting ideas as often as you can:

[http://www.rubyquiz.com/](http://www.rubyquiz.com/)

1. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps  
everyone  
on Ruby T. follow the discussion. Please reply to the original quiz  
message,  
if you can.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Before a credit card is submitted to a financial institution, it  
generally makes  
sense to run some simple reality checks on the number. The numbers are  
a good  
length and it’s common to make minor transcription errors when the card  
is not  
scanned directly.

The first check people often do is to validate that the card matches a  
known  
pattern from one of the accepted card providers. Some of these patterns  
are:

+============+=============+===============+  
| Card Type | Begins With | Number Length |  
+============+=============+===============+  
| AMEX | 34 or 37 | 15 |  
±-----------±------------±--------------+  
| Discover | 6011 | 16 |  
±-----------±------------±--------------+  
| MasterCard | 51-55 | 16 |  
±-----------±------------±--------------+  
| Visa | 4 | 13 or 16 |  
±-----------±------------±--------------+

All of these card types also generate numbers such that they can be  
validated by  
the Luhn algorithm, so that’s the second check systems usually try. The  
steps  
are:

1. Starting with the next to last digit and continuing with every  
other  
digit going back to the beginning of the card, double the digit
2. Sum all doubled and untouched digits in the number
3. If that total is a multiple of 10, the number is valid

For example, given the card number 4408 0412 3456 7893:

Step 1: 8 4 0 8 0 4 2 2 6 4 10 6 14 8 18 3  
Step 2: 8+4+0+8+0+4+2+2+6+4+1+0+6+1+4+8+1+8+3 = 70  
Step 3: 70 % 10 == 0

Thus that card is valid.

Let’s try one more, 4417 1234 5678 9112:

Step 1: 8 4 2 7 2 2 6 4 10 6 14 8 18 1 2 2  
Step 2: 8+4+2+7+2+2+6+4+1+0+6+1+4+8+1+8+1+2+2 = 69  
Step 3: 69 % 10 != 0

That card is not valid.

This week’s Ruby Q. is to write a program that accepts a credit card  
number as  
a command-line argument. The program should print the card’s type (or  
Unknown)  
as well a Valid/Invalid indication of whether or not the card passes the  
Luhn  
algorithm.

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [April 27, 2007, 2:11pm UTC](https://www.ruby-forum.com/t/checking-credit-cards-122/93097/2 "2007-04-27T14:11:32Z")

</div>

On Apr 27, 2007, at 8:59 PM, Ruby Q. wrote:

> 1. Enjoy!  
> Before a credit card is submitted to a financial institution, it  
> patterns are:  
> | Visa | 4 | 13 or 16 |  
> digit going back to the beginning of the card, double the digit
> 
> a command-line argument. The program should print the card’s type  
> (or Unknown)  
> as well a Valid/Invalid indication of whether or not the card  
> passes the Luhn  
> algorithm.

For completeness and to make this Quiz exercise valid to more people,  
can anyone include the information for other major credit cards from  
major countries? Japan: JCB, et. al., U.K.: Barclay, et. al. , etc…  
This would be good to expand the exercise and return more to our  
friends in various areas!  
Discover is itself not found outside of North America, AFIK.

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [April 27, 2007, 2:24pm UTC](https://www.ruby-forum.com/t/checking-credit-cards-122/93097/3 "2007-04-27T14:24:33Z")

</div>

> For completeness and to make this Quiz exercise valid to more people,  
> can anyone include the information for other major credit cards from  
> major countries? Japan: JCB, et. al., U.K.: Barclay, et. al. , etc…

UK had Switch and Solo till recently which are now rebranded under the  
Maestro umbrella. They’re not really credit cards and basically no  
rules exist, you have to check tables to determine valid prefixes and  
card number length. Additionally, Maestro card numbers may also be  
Mastercard numbers, so they can’t be uniquely identified as being  
Maestro.

For JCB and Diner’s:

JCB 3528-2589 Length: 16  
Diners 3000-3029, 3040-3059, 36, 3815-3889, 389 Length: 14

Cheers,  
-Tim

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [April 27, 2007, 2:36pm UTC](https://www.ruby-forum.com/t/checking-credit-cards-122/93097/4 "2007-04-27T14:36:35Z")

</div>

Ruby Q. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) writes:

> This week’s Ruby Q. is to write a program that accepts a credit  
> card number as a command-line argument. The program should print  
> the card’s type (or Unknown) as well a Valid/Invalid indication of  
> whether or not the card passes the Luhn algorithm.

May I make an additional suggestion? Treat spaces in the card number  
as perfectly valid, and simply strip them before processing. One of  
the most irritating features found in many online stores is the  
requirement that the credit card number be typed in with no spaces.  
(The other biggie is using a select box for the state abbreviation)

Now granted, for accepting the card number from the command line this  
will mean that on the command line the card number was quoted, but  
presumably most people’s programs will be a command-line wrapper  
around a function/class that does the real work.

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [April 27, 2007, 3:11pm UTC](https://www.ruby-forum.com/t/checking-credit-cards-122/93097/5 "2007-04-27T15:11:12Z")

</div>

On Apr 27, 2007, at 9:22 PM, Tim B. wrote:

> Maestro.
> 
> For JCB and Diner’s:
> 
> JCB 3528-2589 Length: 16  
> Diners 3000-3029, 3040-3059, 36, 3815-3889, 389 Length: 14
> 
> Cheers,  
> -Tim

Wow, those are some broad ranges.

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [April 27, 2007, 2:55pm UTC](https://www.ruby-forum.com/t/checking-credit-cards-122/93097/6 "2007-04-27T14:55:11Z")

</div>

On Apr 27, 2007, at 7:36 AM, Daniel M. wrote:

> Now granted, for accepting the card number from the command line this  
> will mean that on the command line the card number was quoted…

It doesn’t have to mean that:

$ ruby -e ‘p ARGV.join’ 1111 2222 3333 4444  
“1111222233334444”

James Edward G. II

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [April 27, 2007, 4:16pm UTC](https://www.ruby-forum.com/t/checking-credit-cards-122/93097/7 "2007-04-27T16:16:25Z")

</div>

On Apr 27, 2007, at 10:37 PM, Tim B. wrote:

> still uses them.
> 
> If you think those ranges are broad, the last UK-Switch/Solo table I  
> had has over 180 card prefixes with different card lengths…

My grandmother used to have a Diners Club card. Last time I saw that  
was about 20 years ago too.  
But of the cards accepted here in Tokyo, Diners Club is one that is  
often listed! (in bars and restaurants of course)

Oh, I can only imagine. Processing cards with less deeply thought out  
numbering schemes. The big boys planned that stuff back when  
computing power was a premium.

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [April 27, 2007, 3:38pm UTC](https://www.ruby-forum.com/t/checking-credit-cards-122/93097/8 "2007-04-27T15:38:19Z")

</div>

> > JCB 3528-2589 Length: 16

should be 3528-3589, obviously.

> > Diners 3000-3029, 3040-3059, 36, 3815-3889, 389 Length: 14

> Wow, those are some broad ranges.

Well, those ranges DO include Carte Blanche 🙂 I haven’t seen a  
physical Diners Card in at least 20 years to be honest, not sure who  
still uses them.

If you think those ranges are broad, the last UK-Switch/Solo table I  
had has over 180 card prefixes with different card lengths…

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [April 27, 2007, 8:20pm UTC](https://www.ruby-forum.com/t/checking-credit-cards-122/93097/9 "2007-04-27T20:20:47Z")

</div>

On Apr 27, 2007, at 12:58 PM, Matthew M. wrote:

> > ```
> > +------------+-------------+---------------+
> > 
> > ```
> 
> So is a card number like “4012 3456 7890” a valid Unknown, or an  
> invalid Visa?

Visa Invalid

Always match the type first.

> Similarly, for Unknown, should we accept any length, or go for the  
> common 16?

Unknown is for any type that doesn’t match, so it could be any length.

James Edward G. II

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [April 27, 2007, 7:59pm UTC](https://www.ruby-forum.com/t/checking-credit-cards-122/93097/10 "2007-04-27T19:59:45Z")

</div>

> ```
> +------------+-------------+---------------+
> 
> ```

So is a card number like “4012 3456 7890” a valid Unknown, or an invalid  
Visa?  
Similarly, for Unknown, should we accept any length, or go for the  
common 16?

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [April 27, 2007, 9:32pm UTC](https://www.ruby-forum.com/t/checking-credit-cards-122/93097/11 "2007-04-27T21:32:53Z")

</div>

So I took this as an opportunity to try doing a little TDD, something  
which is appealing but I’ve never really tried. It _is_ fun to see  
failure first and then fix. =) There are probably other tests I  
could have included, but here is a start, in case people want to check  
a few numbers or add more tests:

Obviously, you may need to change this to match your solution…

require “test/unit”  
require “ccard”

class CreditCardCheckTest \< Test::Unit::TestCase

def test\_valid\_numbers  
assert CreditCard.new(“4408041234567893”).valid?  
assert CreditCard.new(“6011111111111117”).valid?  
end

def test\_invalid\_nubers  
assert !CreditCard.new(“4417123456789112”).valid?  
end

def test\_valid\_numbers\_with\_spaces  
assert CreditCard.new(“4408 0412 3456 7893”).valid?  
assert CreditCard.new(“6011 1111 1111 1117”).valid?  
end

def test\_invalid\_nubers\_with\_spaces  
assert !CreditCard.new(“4417 1234 5678 9112”).valid?  
end

def test\_valid\_types  
assert\_equal CreditCard.new(“4408041234567893”).type, :visa  
assert\_equal CreditCard.new(“4408 0412 3456 7893”).type, :visa  
assert\_equal CreditCard.new(“6011 1111 1111 1117”).type, :discover  
assert\_equal CreditCard.new(“123456789”).type, :unknown  
end

def test\_invalid\_types  
assert\_nil CreditCard.new(“4408 0412 3456 789”).type  
end

end

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [April 27, 2007, 11:07pm UTC](https://www.ruby-forum.com/t/checking-credit-cards-122/93097/12 "2007-04-27T23:07:23Z")

</div>

On Apr 27, 2007, at 3:10 PM, anansi wrote:

> would be obsolete don’t you think?  
> Both length and startingbytes have equal weight in the decision  
> wether a card belongs to a known Company or not.

You are right. I didn’t read well. It’s Unknown.

James Edward G. II

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [April 27, 2007, 10:12pm UTC](https://www.ruby-forum.com/t/checking-credit-cards-122/93097/13 "2007-04-27T22:12:18Z")

</div>

James Edward G. II wrote:

> On Apr 27, 2007, at 12:58 PM, Matthew M. wrote:
> 
> > So is a card number like “4012 3456 7890” a valid Unknown, or an  
> > invalid Visa?
> 
> Visa Invalid
> 
> Always match the type first.

Nope thats no Visa number but a unknown one. Visa Cards have a certain  
length (13 or 16) and if that length doesn’t fit (Matthew’s number here  
had just 12 digits)it’s unknown 🙂 otherwise the length would be  
obsolete don’t you think?  
Both length and startingbytes have equal weight in the decision wether a  
card belongs to a known Company or not.

–  
greets  
(  
)  
(  
/\ .-"""-. /  
//\/ , //\  
|/| ,;;;;;, |/|  
//\;-"""-;///\  
// / . / \  
(| ,-_| \ | / |_-, |)  
//` __\.-.-./__ `\  
// /.-(() ())-.\ \  
(\ |) ‘—’ (| /)  
`(| |)`  
jgs ) (/

one must still have chaos in oneself to be able to give birth to a  
dancing star

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [April 28, 2007, 5:48am UTC](https://www.ruby-forum.com/t/checking-credit-cards-122/93097/14 "2007-04-28T05:48:44Z")

</div>

On Apr 27, 2007, at 8:39 PM, Raj S. wrote:

> > rules exist, you have to check tables to determine valid prefixes and  
> > an American Express card that starts with 34 given those starting  
> > numbers.

Tim corrected his typo:

> > > JCB 3528-2589 Length: 16
> 
> should be 3528-3589, obviously.

So I don’t think a leading 34 is ambiguous. It would be American  
Express, with an expected length of 15 digits.

-Mark

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [April 28, 2007, 6:45am UTC](https://www.ruby-forum.com/t/checking-credit-cards-122/93097/15 "2007-04-28T06:45:46Z")

</div>

Mark Day wrote:

> > > card number length. Additionally, Maestro card numbers may also be

Sorry, didn’t see that email for some reason, even though it’s right  
here in my inbox. . .

Raj

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [April 28, 2007, 5:40am UTC](https://www.ruby-forum.com/t/checking-credit-cards-122/93097/16 "2007-04-28T05:40:43Z")

</div>

Tim B. wrote:

> For JCB and Diner’s:
> 
> JCB 3528-2589 Length: 16  
> Diners 3000-3029, 3040-3059, 36, 3815-3889, 389 Length: 14

There’s no way to differentiate between a JCB starting with 34 and an  
American Express card that starts with 34 given those starting numbers.

Raj

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [April 28, 2007, 2:44pm UTC](https://www.ruby-forum.com/t/checking-credit-cards-122/93097/17 "2007-04-28T14:44:16Z")

</div>

On Apr 27, 2007, at 23:06 , James Edward G. II wrote:

> > certain length (13 or 16) and if that length doesn’t fit  
> > (Matthew’s number here had just 12 digits)it’s unknown 🙂  
> > otherwise the length would be obsolete don’t you think?  
> > Both length and startingbytes have equal weight in the decision  
> > wether a card belongs to a known Company or not.
> 
> You are right. I didn’t read well. It’s Unknown.
> 
> James Edward G. II

Do we display Valid/Invalid for unknown cards?

- And by the way: 4012 3456 7890 isn’t valid is it? So should that  
not be “invalid Unknown” or “invalid Visa”?

/C

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [April 28, 2007, 3:11pm UTC](https://www.ruby-forum.com/t/checking-credit-cards-122/93097/18 "2007-04-28T15:11:19Z")

</div>

anansi wrote:

> That’s a unknown and invalid one. To be a Visa one it would need to  
> be  
> 13 or 15 bytes long.

13 or 16 of course…

–  
greets  
(  
)  
(  
/\ .-"""-. /  
//\/ , //\  
|/| ,;;;;;, |/|  
//\;-"""-;///\  
// / . / \  
(| ,-_| \ | / |_-, |)  
//` __\.-.-./__ `\  
// /.-(() ())-.\ \  
(\ |) ‘—’ (| /)  
`(| |)`  
jgs ) (/

one must still have chaos in oneself to be able to give birth to a  
dancing star

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [April 28, 2007, 5:43pm UTC](https://www.ruby-forum.com/t/checking-credit-cards-122/93097/19 "2007-04-28T17:43:30Z")

</div>

On Apr 28, 2007, at 7:43 AM, Christoffer Lernö wrote:

> > >
> 
> Do we display Valid/Invalid for unknown cards?
> 
> - And by the way: 4012 3456 7890 isn’t valid is it? So should that  
> not be “invalid Unknown” or “invalid Visa”?

I would go ahead and display Valid/Invalid normally. That way people  
can use the program on cards we didn’t cover, as long as they conform  
to the standard.

James Edward G. II

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [April 28, 2007, 3:06pm UTC](https://www.ruby-forum.com/t/checking-credit-cards-122/93097/20 "2007-04-28T15:06:18Z")

</div>

Christoffer Lernö wrote:

> > James Edward G. II
> 
> Do we display Valid/Invalid for unknown cards?

We desplay unknown for unkown cards 🙂 and valid/invalid for valid or  
invalid cards, the first has nothing to do with the second.

Check first if it’s a known one which depends on both the length and the  
starting bytes. Then output if it’s a known or unknown one.

After that, check the known or unknown number for validation and output  
that result.

> - And by the way: 4012 3456 7890 isn’t valid is it? So should that not  
> be “invalid Unknown” or “invalid Visa”?
> 
> /C

That’s a unknown and invalid one. To be a Visa one it would need to be  
13 or 15 bytes long.

–  
greets  
(  
)  
(  
/\ .-"""-. /  
//\/ , //\  
|/| ,;;;;;, |/|  
//\;-"""-;///\  
// / . / \  
(| ,-_| \ | / |_-, |)  
//` __\.-.-./__ `\  
// /.-(() ())-.\ \  
(\ |) ‘—’ (| /)  
`(| |)`  
jgs ) (/

one must still have chaos in oneself to be able to give birth to a  
dancing star

[Next page](https://www.ruby-forum.com/t/checking-credit-cards-122/93097.md?page=2)
