Generating Luhn Checksums
your php is buggy, it leads into an infinite loop. This is the working version that I'm using, modified from your code
function Luhn($number) {
$stack = 0; $number = str_split(strrev($number)); foreach ($number as $key => $value) { if ($key % 2 == 0) { $value = array_sum(str_split($value * 2)); } $stack += $value; } $stack %= 10; if ($stack != 0) { $stack -= 10; $stack = abs($stack); } $number = implode('', array_reverse($number)); $number = $number . strval($stack); return $number;
}
Create a php and run in your localhost Luhn(xxxxxxxx) to confirm.
Edit: Sorry, I realize now that you had almost my entire answer already, you had just incorrectly determined which factor to use for which digit.
My entire answer now can be summed up with this single sentence:
You have the factor reversed, you're multiplying the wrong digits by 2 depending on the length of the number.
Take a look at the Wikipedia article on the Luhn algorithm.
The reason your checksum is invalid half the time is that with your checks, half the time your number has an odd number of digits, and then you double the wrong digit.
For 37283, when counting from the right, you get this sequence of numbers:
3 * 1 = 3 3
8 * 2 = 16 --> 1 + 6 = 7
2 * 1 = 2 2
7 * 2 = 14 --> 1 + 4 = 5
+ 3 * 1 = 3 3
= 20
The algorithm requires you to sum the individual digits from the original number, and the individual digits of the product of those "every two digits from the right".
So from the right, you sum 3 + (1 + 6) + 2 + (1 + 4) + 3, which gives you 20.
If the number you end up with ends with a zero, which 20 does, the number is valid.
Now, your question hints at you wanting to know how to generate the checksum, well, that's easy, do the following:
- Tack on an extra zero, so your number goes from xyxyxyxy to xyxyxyxy0
- Calculate the luhn checksum sum for the new number
- Take the sum, modulus 10, so you get a single digit from 0 to 10
- If the digit is 0, then congratulations, your checksum digit was a zero
- Otherwise, calculate 10-digit to get what you need for the last digit, instead of that zero
Example: Number is 12345
- Tack on a zero: 123450
Calculate the luhn checksum for 123450, which results in
0 5 4 3 2 1 1 2 1 2 1 2 <-- factor 0 10 4 6 2 2 <-- product 0 1 0 4 6 2 2 <-- sum these to: 0+1+0+4+6+2+2=15
Take the sum (15), modulus 10, which gives you 5
- Digit (5), is not zero
- Calculate 10-5, which gives you 5, the last digit should be 5.
So the result is 123455.
BAD
I literally cannot believe how many crummy implementations there are out there.
IDAutomation has a .NET assembly with a MOD10() function to create but it just doesn't seem to work. In Reflector the code is way too long for what it's supposed to be doing anyway.
BAD
This mess of a page which is actually currently linked to from Wikipedia(!) for Javascript has several verification implementations that don't even return the same value when I call each one.
GOOD
The page linked to from Wikipedia's Luhn page has a Javascript encoder which seems to work :
// Javascript
String.prototype.luhnGet = function()
{
var luhnArr = [[0,1,2,3,4,5,6,7,8,9],[0,2,4,6,8,1,3,5,7,9]], sum = 0;
this.replace(/\D+/g,"").replace(/[\d]/g, function(c, p, o){
sum += luhnArr[ (o.length-p)&1 ][ parseInt(c,10) ]
});
return this + ((10 - sum%10)%10);
};
alert("54511187504546384725".luhnGet());
GOOD
This very useful EE4253 page verifies the check-digit and also shows the full calculation and explanation.
GOOD
I needed C# code and ended up using this code project code:
// C#
public static int GetMod10Digit(string data)
{
int sum = 0;
bool odd = true;
for (int i = data.Length - 1; i >= 0; i--)
{
if (odd == true)
{
int tSum = Convert.ToInt32(data[i].ToString()) * 2;
if (tSum >= 10)
{
string tData = tSum.ToString();
tSum = Convert.ToInt32(tData[0].ToString()) + Convert.ToInt32(tData[1].ToString());
}
sum += tSum;
}
else
sum += Convert.ToInt32(data[i].ToString());
odd = !odd;
}
int result = (((sum / 10) + 1) * 10) - sum;
return result % 10;
}
GOOD
This validation code in C# seems to work, if a little unwieldy. I just used it to check the above was correct.