Credit card type and luhn check in ruby

I was looking at implementing a luhn and credit card type check the other day in java and I noticed that there seems to be a lack of code for doing this in ruby. So I figured I would put something together for doing the checks in ruby.

The following function will do a luhn check for a given number (any number not just credit card numbers). The luhn algorithm is fairly simple, if you want to learn more about it check here.

def luhnCheck(ccNumber)
  ccNumber = ccNumber.gsub(/\\D/, '')
  cardLength = ccNumber.length
  parity = cardLength % 2

  sum = 0
  for i in 0...cardLength
    digit = ccNumber[i] - 48

    if i % 2 == parity
      digit = digit * 2
    end

    if digit > 9
      digit = digit - 9
    end

    sum = sum + digit
  end

  return (sum % 10) == 0
end

Before running the luhn check you may want to verify that you have a valid card type or at least one you want to accept. The following function will do that based on the current bin ranges for the differenct companies as of today (for more on this see the following: credit card number information and BIN range information). N.B. Bin ranges change from time to time so this will become dated. It should be easy enough to find the updated ranges.

def ccTypeCheck(ccNumber)
  ccNumber = ccNumber.gsub(/\\D/, '')
  case ccNumber
    when /^3[47]\\d{13}$/ then return "AMEX"
    when /^4\\d{12}(\\d{3})?$/ then return "VISA"
    when /^5\\d{15}|36\\d{14}$/ then return "MC"
    when /^6011\\d{12}|650\\d{13}$/ then return "DISC"
    when /^3(0[0-5]|8[0-1])\\d{11}$/ then return "DINERS"
    when /^(39\\d{12})|(389\\d{11})$/ then return "CB"
    when /^3\\d{15}|1800\\d{11}|2131\\d{11}$/ then return "JCB"
    else return "NA"
  end
end
del.icio.us:Credit card type and luhn check in ruby  digg:Credit card type and luhn check in ruby  spurl:Credit card type and luhn check in ruby  wists:Credit card type and luhn check in ruby  simpy:Credit card type and luhn check in ruby  newsvine:Credit card type and luhn check in ruby  blinklist:Credit card type and luhn check in ruby  furl:Credit card type and luhn check in ruby  reddit:Credit card type and luhn check in ruby  fark:Credit card type and luhn check in ruby  blogmarks:Credit card type and luhn check in ruby  Y!:Credit card type and luhn check in ruby  smarking:Credit card type and luhn check in ruby  magnolia:Credit card type and luhn check in ruby  segnalo:Credit card type and luhn check in ruby  gifttagging:Credit card type and luhn check in ruby

6 Responses to “Credit card type and luhn check in ruby”

  1. darrend Says:

    A Ruby method without a coroutine? I just can’t stand it…

    Seriously though, I tried to give this one a bit more of the Ruby sass…so far it matches the results of the original.

    Of course, it is a judgement call which is easier to read and to follow.

    I think my only real claim to an improvement is that now the core of the algorithm is normalized to accept an Enumerable of numeric values; slightly more reusable maybe someday in some context?

    
    def luhnother(ccNumber)
      ccNumber = ccNumber.gsub(/D/,'').split(//).collect { |digit| digit.to_i }
      parity = ccNumber.length % 2
    
      sum = 0
      ccNumber.each_with_index do |digit,index|
        digit = digit * 2 if index%2==parity
        digit = digit - 9 if digit > 9
        sum = sum + digit
      end
    
      return (sum%10)==0
    end
    
  2. wrighty Says:

    Just a small point, that regexp only removes capital Ds, it should really be /\D/ instead.

  3. pvdb Says:

    In addition to wrighty’s comment:

    Another small point, but in all regexps in the ccTypeCheck() method, the lowercase “d”s should all really be “\d” (ie. the digit character class for Ruby regexps)

    (I guess the ruby source code on this page gets mangled when it is rendered as HTML by the blogging software, or some such thing, and the backslashes removed in the process)

  4. James Says:

    Sir,
    How can the extraction of name on a credit card be made? your urgent response Sir. Thanks.

  5. Rick Hull Says:

    Here is an even shorter version. It relies on the fact that the check digit (rightmost) is always considered odd. So we reverse the number and toggle the odd flag on each accumulation iteration.


    def valid_luhn?(num)
    num = num.to_s.reverse.split(//).map { |d| d.to_i }
    odd = false
    num.inject(0) { |memo, i|
    memo + ((odd = !odd) ? i : (i*2 > 9 ? i*2 - 9 : i*2))
    } % 10 == 0
    end

  6. David Lowenfels Says:

    here’s my version:


    def check_digits? num
    odd = true
    num.to_s.gsub(/\D/,'').reverse.split('').map(&:to_i).collect { |d|
    d *= 2 if odd = !odd
    d > 9 ? d - 9 : d
    }.sum % 10 == 0
    end

Leave a Reply