Skip to content

Instantly share code, notes, and snippets.

@eegrok
Created September 13, 2012 09:30
Show Gist options
  • Select an option

  • Save eegrok/3713188 to your computer and use it in GitHub Desktop.

Select an option

Save eegrok/3713188 to your computer and use it in GitHub Desktop.
CC Validation

Example Usage -- use ruby 1.9.3

git clone git@gist.github.com:3713188.git ccvalidator

cd ccvalidator

cat input1.dat | ./validate_cards.rb

ruby test/credit_card_validator_spec.rb

# note -- assumption is that an Unknown type of card is invalid, regardless of the luhn validity
# I also consider a card to be of Unknown type if the length doesn't match the valid length for a given prefix
class CreditCardValidator
attr_accessor :cardnum
def initialize(cardnum)
#remove spaces and dashes -- possibly should remove all non-digit characters?
@cardnum = cardnum.gsub(/[\s-]/, '')
end
# check prefix digits and length
def card_type
return 'AMEX' if ['34','37'].include?(@cardnum[0..1]) && @cardnum.length == 15
return 'Discover' if @cardnum[0..3] == '6011' && @cardnum.length == 16
return 'MasterCard' if ['51','52','53','54','55'].include?(@cardnum[0..1]) && @cardnum.length == 16
return 'VISA' if @cardnum[0..0] == '4' && [13,16].include?(@cardnum.length)
return 'Unknown'
end
def type_and_number
"#{card_type}: #{cardnum}"
end
# check that the card number matches the luhn algorithm
def valid_luhn
# for odd length card numbers, we want to start by doubling the second digit instead of the first one
offset = (@cardnum.length + 1) % 2
expanded_string = ''
@cardnum.chars.each_with_index do |char, index|
# double every other digit
new_char = char.to_i * ((index + offset) % 2 + 1)
expanded_string += new_char.to_s
end
sum_of_chars = expanded_string.chars.inject(0) do |result, char|
result += char.to_i
end
return (sum_of_chars % 10) == 0
end
def valid
return card_type != 'Unknown' && valid_luhn
end
def printable_valid
valid ? 'valid' : 'invalid'
end
def self.process_stream(istream)
while !istream.eof && line = istream.readline
validator = CreditCardValidator.new(line.strip)
yield "#{validator.type_and_number.ljust(28)} (#{validator.printable_valid})"
end
end
end
require 'minitest/autorun'
require 'stringio'
require_relative '../credit_card_validator.rb'
describe "CreditCardValidator", "Complete credit card validation tests" do
it "validates AMEX cards" do
#test valid cases
validator = CreditCardValidator.new("378282246310005")
validator.card_type.must_equal 'AMEX'
validator.valid.must_equal true
validator = CreditCardValidator.new("348282246310002")
validator.card_type.must_equal 'AMEX'
validator.valid.must_equal true
validator = CreditCardValidator.new("378282246310005")
validator.card_type.must_equal 'AMEX'
validator.valid.must_equal true
#test invalid prefix
validator = CreditCardValidator.new("358282246310017")
validator.card_type.must_equal 'Unknown'
validator.valid_luhn.must_equal true
validator.valid.must_equal false
#test invalid luhn
validator = CreditCardValidator.new("348282246310016")
validator.card_type.must_equal 'AMEX'
validator.valid.must_equal false
#test invalid length
validator = CreditCardValidator.new("358282246310017")
validator.card_type.must_equal 'Unknown'
validator.valid_luhn.must_equal true
validator.valid.must_equal false
end
it "validates Mastercards" do
#test valid cases
validator = CreditCardValidator.new("5103195004195007")
validator.card_type.must_equal 'MasterCard'
validator.valid.must_equal true
validator = CreditCardValidator.new("5203195004195006")
validator.card_type.must_equal 'MasterCard'
validator.valid.must_equal true
validator = CreditCardValidator.new("5303195004195005")
validator.card_type.must_equal 'MasterCard'
validator.valid.must_equal true
validator = CreditCardValidator.new("5403195004195004")
validator.card_type.must_equal 'MasterCard'
validator.valid.must_equal true
validator = CreditCardValidator.new("5503195004195003")
validator.card_type.must_equal 'MasterCard'
validator.valid.must_equal true
#test invalid prefix
validator = CreditCardValidator.new("5003195004195008")
validator.card_type.must_equal 'Unknown'
validator.valid_luhn.must_equal true
validator.valid.must_equal false
#test invalid luhn
validator = CreditCardValidator.new("5503195004195004")
validator.card_type.must_equal 'MasterCard'
validator.valid.must_equal false
#test invalid length
validator = CreditCardValidator.new("55031950041953")
validator.card_type.must_equal 'Unknown'
validator.valid_luhn.must_equal true
validator.valid.must_equal false
end
it "validates Discover cards" do
#test valid cases
validator = CreditCardValidator.new("6011111111111109")
validator.card_type.must_equal 'Discover'
validator.valid.must_equal true
#test invalid prefix
validator = CreditCardValidator.new("7011111111111107")
validator.card_type.must_equal 'Unknown'
validator.valid_luhn.must_equal true
validator.valid.must_equal false
#test invalid luhn
validator = CreditCardValidator.new("6011111111111119")
validator.card_type.must_equal 'Discover'
validator.valid.must_equal false
#test invalid length
validator = CreditCardValidator.new("60111111111110")
validator.card_type.must_equal 'Unknown'
validator.valid_luhn.must_equal true
validator.valid.must_equal false
end
it "validates Visa cards" do
#test valid cases
validator = CreditCardValidator.new("4211111111111110")
validator.card_type.must_equal 'VISA'
validator.valid.must_equal true
validator = CreditCardValidator.new("4211 1111 1111 1110")
validator.card_type.must_equal 'VISA'
validator.valid.must_equal true
validator = CreditCardValidator.new("4211-1111-1111-1110")
validator.card_type.must_equal 'VISA'
validator.valid.must_equal true
validator = CreditCardValidator.new("4000000000006")
validator.card_type.must_equal 'VISA'
validator.valid.must_equal true
#test invalid prefix
validator = CreditCardValidator.new("3111111111111113")
validator.card_type.must_equal 'Unknown'
validator.valid_luhn.must_equal true
validator.valid.must_equal false
#test invalid luhn
validator = CreditCardValidator.new("4111111111111110")
validator.card_type.must_equal 'VISA'
validator.valid.must_equal false
#test invalid length
validator = CreditCardValidator.new("400000000002")
validator.card_type.must_equal 'Unknown'
validator.valid_luhn.must_equal true
validator.valid.must_equal false
end
it "processes streams correctly" do
inputfile = StringIO.new("4111111111111111\n4111111111111\n4012888888881881\n378282246310005\n6011111111111117\n5105105105105100\n5105 1051 0510 5106\n9111111111111111\n")
outputfile = StringIO.new("VISA: 4111111111111111 (valid)\nVISA: 4111111111111 (invalid)\nVISA: 4012888888881881 (valid)\nAMEX: 378282246310005 (valid)\nDiscover: 6011111111111117 (valid)\nMasterCard: 5105105105105100 (valid)\nMasterCard: 5105105105105106 (invalid)\nUnknown: 9111111111111111 (invalid)\n")
result = CreditCardValidator.process_stream(inputfile) do |line|
line.must_equal outputfile.readline.strip
end
end
end
4111111111111111
4111111111111
4012888888881881
378282246310005
6011111111111117
5105105105105100
5105 1051 0510 5106
9111111111111111
#!/usr/bin/env ruby
require_relative 'credit_card_validator'
CreditCardValidator.process_stream(STDIN) do |result|
STDOUT << result << "\n"
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment