Skip to content

Instantly share code, notes, and snippets.

@ShirtlessKirk
Last active February 12, 2024 05:09
Show Gist options
  • Save ShirtlessKirk/2134376 to your computer and use it in GitHub Desktop.
Save ShirtlessKirk/2134376 to your computer and use it in GitHub Desktop.
Luhn validation algorithm
/**
* Luhn algorithm in JavaScript: validate credit card number supplied as string of numbers
* @author ShirtlessKirk. Copyright (c) 2012.
* @license WTFPL (http://www.wtfpl.net/txt/copying)
*/
var luhnChk = (function (arr) {
return function (ccNum) {
var
len = ccNum.length,
bit = 1,
sum = 0,
val;
while (len) {
val = parseInt(ccNum.charAt(--len), 10);
sum += (bit ^= 1) ? arr[val] : val;
}
return sum && sum % 10 === 0;
};
}([0, 2, 4, 6, 8, 1, 3, 5, 7, 9]));
@storytime
Copy link

Awesome!

@MohamedAlaa
Copy link

Woww! So far the Fastest Algorithm i've tried! 👍

@oPorks
Copy link

oPorks commented Jul 28, 2014

Well done!

@lagden
Copy link

lagden commented Aug 14, 2014

Fuckk Yeahhh!!!

@ShirtlessKirk
Copy link
Author

Spot of code golf: After reviewing subsequent edits on jsperf, I've tweaked the code. The test case is at http://jsperf.com/credit-card-validator/16 if you want to check.

  • decrement now happens inline rather than during the while conditional check (I prefer that placement)
  • reduction of product array contents to just the doubled number value as you've already got the undoubled value (less to initialise, less to look up)
  • the use of a closure to use the product array as a parameter was intriguing
  • return statement reversed to utilise short-circuiting better (if a zero length string is sent in then the sum is 0, cosmetic really)

A variant on the jsperf page doesn't run parseInt() if using the undoubled value. Personally, I don't like using it in some places and not others so I always run it. That's why this code isn't the fastest...

@ChadReitsma
Copy link

Ummm, WOW. TY!!!!

@WebSellService
Copy link

I am new, any one can tell me how can i use this code?
I want to validate cards.
and this code can validate all type of cards?

@lebiru
Copy link

lebiru commented May 14, 2015

+1 thanks!

@alesch
Copy link

alesch commented May 25, 2015

@geniuscarpi: Use it as luhnChk("numberToValidate").

@tomByrer
Copy link

tomByrer commented Aug 6, 2015

I'm a bit confused the reason for bit ^= 1, since it seems that bit never changes? (version Feb 11, 2015)

@jlukic
Copy link

jlukic commented Aug 13, 2015

Using an adapted version of your luhn check in our validation
Semantic-Org/Semantic-UI@2769a6e

@liqiang372
Copy link

Hey, I passed a test with 7 digits 2222222. Isn't valid credit card has digits range from 12 to 19? is this a bug?

@ShirtlessKirk
Copy link
Author

@tomByrer: yeah it does. ^= flips the value. It's a bitwise operator, not a Boolean one (see MDN page)

@CeleryCup: this is just a Luhn algorithm (see Wikipedia). Apart from checking for zero length strings there is no range checking as that is outside the scope of the method and checking for credit card number validity is just one application of it. For example, you could set up a login verification solution using identifier numbers that must pass Luhn to be valid.

@thensg
Copy link

thensg commented Nov 2, 2015

I decided to adapt your code, but add support for creating a Luhn checksum. You can find the changes here: https://gist.github.com/thensg/07bd82f73a1f784a35f0

@ChadReitsma
Copy link

MasterCard announced new bin ranges (222100 - 272099) in November 2014, and will be introduced into the payment eco-system on October 2016, not sure if this validates them, but I'm assuming it will.

@enapupe
Copy link

enapupe commented Dec 2, 2015

There is a small issue regarding user input. The input MUST be a string, otherwise the return is always 0

@ShirtlessKirk
Copy link
Author

@enapupe: well, yeah. Credit card numbers aren't actual integers but a string of integers; every Luhn implementation expects a string to work with. Not only that, line 2 even says so...
As for returning 0, that's a falsey value, so the function still "works" ;)

@nani3105
Copy link

Wow, Impressive

@biranchi2018
Copy link

The luhn test is failing for the MasterCard BIN 2-Series (222100-272099)

Sample Master Card:
2221 0012 3412 3456,
Expiry : 12/18

@ShirtlessKirk
Copy link
Author

@afixibiranchi I see that your issue was answered on http://planetcalc.com/2464/ . I'm guessing you used the "number" on the example card image of https://www.mastercard.us/en-us/issuers/get-support/2-series-bin-expansion.html where it would make sense to be an invalid number...

@heymartinadams
Copy link

heymartinadams commented Sep 29, 2017

Wonderful! Thank you.

@eugene-chernogradski
Copy link

eugene-chernogradski commented Oct 26, 2017

@ShirtlessKirk OMFG
Phil, dude, who could've imagined it'would be your implementation of Luhn check I'm gonna use?!

@jonbodner
Copy link

@ShirtlessKirk I'd like to use this luhn implementation, but my employer forbids software licensed under WTFPL. Can you relicense or dual-license under BSD or MIT?

@JurgenBlitz
Copy link

Wonderful implementation. I worked a very basic solution to adapt this algorithm to TypeScript, feel free to use or comment on improvements:
`
public luhnAlgorithmCheck(ccNum) {
const arr = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9];
let len = ccNum.length;
let bit = 1;
let sum = 0;
let val;

while (len) {
  val = parseInt(ccNum.charAt(--len), 10);
  // tslint:disable-next-line:no-bitwise
  sum += (bit ^= 1) ? arr[val] : val;
}
return sum && sum % 10 === 0;

}
`
Thanks for sharing the original code, @ShirtlessKirk

@jensb1
Copy link

jensb1 commented Mar 12, 2020

"12345678".split("").reverse().map(function(c, i) { return ((i%2==0 ? 1 : 2)*parseInt(c)) }).join("").split("").map((c) => parseInt(c)).reduce(function(sum, c) { return sum+=parseInt(c) })

@emandeguzman
Copy link

emandeguzman commented Apr 23, 2020

I tested against https://www.paypalobjects.com/en_US/vhelp/paypalmanager_help/credit_card_numbers.htm

76009244561 did not return true

But I'm not sure if this number is really valid. all luhn verification code seem to fail when I use this number.

@jefelewis
Copy link

@ShirtlessKirk What does the array at the end do?

}([0, 2, 4, 6, 8, 1, 3, 5, 7, 9]));

@ShirtlessKirk
Copy link
Author

@jefelewis it's a lookup table. Basically, instead of calculating the summed result of the numbers that are doubled (every second digit from right) each time, the code uses the precalculated value stored in the array at the index of the digit in question.

For example, if the digit is 6, the summed double is 3 (6 * 2 = 12, 1 + 2 = 3). In the array, the value at index 6 (the original digit used as a reference) is set to 3.

@bdr193
Copy link

bdr193 commented May 10, 2022

@ShirtlessKirk Can this be implemented in Shopify or Woocommerce? How can you install it without having access to the payment iframe

@carlosvega20
Copy link

Declarative/functional approach:

const checkLuhn = cardNumber => {
    const sum = [...cardNumber].reduceRight((prev, curr, i, arr) =>
        prev+= (i%2)?Number(arr[Number(curr)]):Number(curr)
    ,0);
    return sum && sum % 10 === 0;
}

https://gist.github.com/carlosvega20/8ec8b472de22626a70a65f5893de82e9?permalink_comment_id=4748205#gistcomment-4748205

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment