Instantly share code, notes, and snippets.

# ShirtlessKirk/luhn.js

Last active May 17, 2024 08:05
Show Gist options
• Save ShirtlessKirk/2134376 to your computer and use it in GitHub Desktop.
Luhn validation algorithm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
 /** * 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]));

### liqiang372 commented Sep 9, 2015

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 commented Oct 27, 2015

@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 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 commented Nov 30, 2015

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 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 commented Mar 18, 2016

@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" ;)

Wow, Impressive

### biranchi2018 commented Sep 21, 2016

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

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

### ShirtlessKirk commented Oct 11, 2016

@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...

Wonderful! Thank you.

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

### jonbodner commented Feb 5, 2018

@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 commented Jul 10, 2019

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 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) })`

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 commented Oct 1, 2021

@ShirtlessKirk What does the array at the end do?

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

### ShirtlessKirk commented Oct 6, 2021

@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 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 commented Nov 3, 2023

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;
}
``````

@carlosvega20 I'm a bit confused by `Number(arr[Number(curr)])` expression and not sure if this works correctly for check

Can suggest instead smth like this:

``````const isLuhn = (cardNumber) => {
const checkSum = [...cardNumber].reduceRight((prev, curr, i, arr) => {
if(i % 2) {
return prev += Number(curr);
} else {
const d  = Number(curr) * 2
return prev += d > 9 ? d - 9 : d
}
}, 0);

return checkSum && checkSum % 10 === 0
}
``````