Skip to content

Instantly share code, notes, and snippets.

@ShirtlessKirk
Last active December 31, 2025 09:18
Show Gist options
  • Select an option

  • Save ShirtlessKirk/2134376 to your computer and use it in GitHub Desktop.

Select an option

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]));
@JurgenBlitz

Copy link
Copy Markdown

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

jensb1 commented Mar 12, 2020

Copy link
Copy Markdown

"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

emandeguzman commented Apr 23, 2020

Copy link
Copy Markdown

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
Copy Markdown

@ShirtlessKirk What does the array at the end do?

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

@ShirtlessKirk

Copy link
Copy Markdown
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

bdr193 commented May 10, 2022

Copy link
Copy Markdown

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

@carlosvega20

Copy link
Copy Markdown

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

@Yana-Lesina

Yana-Lesina commented May 17, 2024

Copy link
Copy Markdown

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

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