This is not meant to be an exhaustive list of all possible numbers, nor the only or best method to verify that they pass the "checksum" test, but here's what I came up with.
I wrote this mostly to link a Ruby version of the code to Wikipedia's article on Luhn checksum validation, since nearly every other language in use was listed, but Ruby was sadly missing.
#!/usr/bin/env ruby
#
# Copyright (c) 2008 Michael Graff. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# 3. The name of Michael Graff may not be used to endorse or promote
# products derived from this software without specific prior
# written permission.
#
# THIS SOFTWARE IS PROVIDED BY Michael Graff ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Micahel Graff
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
# OF SUCH DAMAGE.
#
class Luhn
public
def self.check_luhn(s)
s.gsub!(/[^0-9]/, "")
ss = s.reverse.split(//)
alternate = false
total = 0
ss.each do |c|
if alternate
total += double_it(c.to_i)
else
total += c.to_i
end
alternate = !alternate
end
(total % 10) == 0
end
private
def self.double_it(i)
i = i * 2
if i > 9
i = i % 10 + 1
end
i
end
end
if $0 == __FILE__
def test_valid(s)
result = Luhn::check_luhn(s)
if result
puts "VALID: #{s}"
else
puts "INVALID: #{s} (should be valid)"
end
end
test_valid('5105 1051 0510 5100') # Mastercard
test_valid('5555 5555 5555 4444') # Mastercard
test_valid('4222 2222 2222 2') # Visa
test_valid('4111 1111 1111 1111') # Visa
test_valid('4012 8888 8888 1881') # Visa
test_valid('3782 8224 6310 005') # American Express
test_valid('3714 4963 5398 431') # American Express
test_valid('3787 3449 3671 000') # American Express Corporate
test_valid('3782 8224 6310 005') # Amex
test_valid('3400 0000 0000 009') # Amex
test_valid('3700 0000 0000 002') # Amex
test_valid('38520000023237') # Diners Club (14 digits)
test_valid('30569309025904') # Diners Club (14 digits)
test_valid('6011111111111117') # Discover (16 digits)
test_valid('6011 0000 0000 0004') # Discover
test_valid('6011 0000 0000 0012') # Discover
test_valid('6011000990139424') # Discover (16 digits)
test_valid('6011601160116611') # Discover (16 digits)
test_valid('3530111333300000') # JCB (16 digits)
test_valid('3566002020360505') # JCB (16 digits)
test_valid('5431111111111111') # Mastercard (16 digits)
end