Luhn’s algorithm - verify credit cards - GO
Tags: go, computerscience
Published: 2020-09-15 00:27:47

Check out the repository
[repo](https://github.com/georgexx009/validate_credit_card)

The explanation is from course CS50 from HarvardX, the algorithm in GO is my approach. It just uses mathematics.

Algorithm from Hans Peter Luhn of IBM for validate credit cards in GO
A credit (or debit) card, of course, is a plastic card with which you can pay for goods and services. Printed on that card is a number that’s also stored in a database somewhere, so that when your card is used to buy something, the creditor knows whom to bill. There are a lot of people with credit cards in this world, so those numbers are pretty long: American Express uses 15-digit numbers, MasterCard uses 16-digit numbers, and Visa uses 13- and 16-digit numbers. And those are decimal numbers (0 through 9), not binary, which means, for instance, that American Express could print as many as 10^15 = 1,000,000,000,000,000 unique cards! (That’s, um, a quadrillion.)

Actually, that’s a bit of an exaggeration, because credit card numbers actually have some structure to them. All American Express numbers start with 34 or 37; most MasterCard numbers start with 51, 52, 53, 54, or 55 (they also have some other potential starting numbers which we won’t concern ourselves with for this problem); and all Visa numbers start with 4. But credit card numbers also have a “checksum” built into them, a mathematical relationship between at least one number and others. That checksum enables computers (or humans who like math) to detect typos (e.g., transpositions), if not fraudulent numbers, without having to query a database, which can be slow. Of course, a dishonest mathematician could certainly craft a fake number that nonetheless respects the mathematical constraint, so a database lookup is still necessary for more rigorous checks.
Luhn’s Algorithm

So what’s the secret formula? Well, most cards use an algorithm invented by Hans Peter Luhn of IBM. According to Luhn’s algorithm, you can determine if a credit card number is (syntactically) valid as follows:

    Multiply every other digit by 2, starting with the number’s second-to-last digit, and then add those products’ digits together.
    Add the sum to the sum of the digits that weren’t multiplied by 2.
    If the total’s last digit is 0 (or, put more formally, if the total modulo 10 is congruent to 0), the number is valid!

That’s kind of confusing, so let’s try an example with David’s Visa: 4003600000000014.

    For the sake of discussion, let’s first underline every other digit, starting with the number’s second-to-last digit:

    4003600000000014

    Okay, let’s multiply each of the underlined digits by 2:

    1•2 + 0•2 + 0•2 + 0•2 + 0•2 + 6•2 + 0•2 + 4•2

    That gives us:

    2 + 0 + 0 + 0 + 0 + 12 + 0 + 8

    Now let’s add those products’ digits (i.e., not the products themselves) together:

    2 + 0 + 0 + 0 + 0 + 1 + 2 + 0 + 8 = 13

    Now let’s add that sum (13) to the sum of the digits that weren’t multiplied by 2 (starting from the end):

    13 + 4 + 0 + 0 + 0 + 0 + 0 + 3 + 0 = 20

    Yup, the last digit in that sum (20) is a 0, so David’s card is legit!

So, validating credit card numbers isn’t hard, but it does get a bit tedious by hand.


```GO
package main

import (
	"fmt"
	"strconv"
)

// 4003600000000014 - VISA
// it doesn't use arrays or struct
// pure mathematic solution

func main() {
	// get card number
	var cardNumber int = -1
	for cardNumber == -1 {
		cardNumber = getCardNumer()
	}

	// get the number of digits from the card
	numDigits := getNumberDigitsCard(cardNumber)

	// AUX VARIABLES ---------------------------
	// aux vars for iterate through the card number
	mod := 10
	divider := 1
	// aux for know the ieration, because it goes from right to left
	var generalOdd bool = (numDigits % 2) != 0
	// aux var for iterate one yes - one no
	odd := generalOdd
	// accumulators
	ac1 := 0 // special - the one that will be multiple by 2
	ac2 := 0 // odds - start from the second digit

	// ITERATE CARD NUMBER
	for cardNumber > divider {

		// get the digit from the right
		var currentVal int = (cardNumber % mod) / divider

		// move one digit to the left
		mod = mod * 10
		divider = divider * 10

		// LOGIC FROM Hans Peter Luhn ALGORITHM
		// make the sum step from digits
		if odd {
			if !generalOdd {
				ac1 = ac1 + specialSum(currentVal)
			} else {
				ac1 = ac1 + currentVal
			}
		} else {
			if generalOdd {
				ac2 = ac2 + specialSum(currentVal)
			} else {
				ac2 = ac2 + currentVal
			}
		}
		odd = !odd
	}
	var result int = ac1 + ac2

	// CHECK THE RESULT
	// compare the info given from the credit cards
	var cardType string = checkResult(result, numDigits, cardNumber)
	fmt.Println(cardType)
}

func getCardNumer() int {
	// get the value
	var str string = ""
	fmt.Print("Enter a card number: ")
	fmt.Scanf("%s", &str)

	// convert value
	cardNumber, err := strconv.Atoi(str)

	// handle error if no number was provided
	if err != nil {
		fmt.Println("Enter only numbers")
		return -1
	}

	return cardNumber
}

func getNumberDigitsCard(card int) (numDigits int) {
	divider := 10
	// because is an int, will delete the decimals from each division
	for card != 0 {
		card = card / divider
		numDigits += 1
	}
	return
}

// SPECIAL SUM
// because the log says if multiple by 2 is equal to a number with two digits
// we should sum the two digits from the number
func specialSum(num int) int {
	if num < 5 {
		return num * 2
	} else {
		num = num * 2
		var n1 int = num % 10
		var n2 int = num / 10
		return n1 + n2
	}
}

// CHECK THE RESULT
// compare the info given from the credit cards
func checkResult(result, numDigits, cardNumber int) string {
	if (result % 10) == 0 {
		var divisor int = 1
		for i := 0; i < numDigits-2; i++ {
			divisor = 10 * divisor
		}
		var firstDigits int = cardNumber / divisor
		var firstDigit = firstDigits / 10
		if firstDigits == 34 || firstDigits == 37 {
			if numDigits == 15 {
				return "AMEX"
			} else {
				return "INVALID"
			}
		} else if firstDigits == 51 || firstDigits == 52 || firstDigits == 53 || firstDigits == 54 || firstDigits == 55 {
			if numDigits == 16 {
				return "MASTERCARD"
			} else {
				return "INVALID"
			}
		} else if firstDigit == 4 {
			if numDigits == 13 || numDigits == 16 {
				return "VISA"
			} else {
				return "INVALID"
			}
		}
	}
	return "INVALID"
}

```