Simplified Credit Card Validator API in Go
A credit card validation process is always needed for developers building a product with a checkout section. This is not a very complex project but it will show how to build an API where one endpoint validates the incoming card number. So let’s get started and build a minimal credit card validator API using Go.
Initially let’s start by understanding the Luhn algorithm and where it is used. Then we will build an API that will serve the validation functionality.
For the full code, check the repository out here.
Luhn Algorithm
The algorithm was first developed by Hans Peter Luhn at IBM. It is widely used for validation processes. Some examples are credit card numbers, IMEI numbers, some countries’ ID numbers, account numbers, and more.
It’s important to note that the algorithm is not used to be secure. The main idea is validation and only validation. Please do not use it for security.
The algorithm processes the number in reverse order. There is a check digit located at the end of the whole number. The rest of the numbers are used to calculate the check digit. The process starts from where the check digit is and calculates towards the other end. The process is as follows:
- Locate the check digit and save it.
- Create a running sum, and set it to 0.
- Go to the next number and multiply every other number by 2.
- If the digit is multiplied by 2, add the digits to the running sum individually.
- If not, add the digit to the running sum.
- Repeat step 3 until all digits are processed.
- To calculate the check digit take the running sum take the modulo (remainder of division) 10, and subtract from 10. Mathematically this is 10 – (Running sum % 10).
- Compare the digit saved in step 1 to the calculated digit in step 7.
Example Calculation
Digits | 5 | 6 | 5 | 2 | 1 | 8 | 3 | 4 | 7 | 2 |
Multiplier | 1 | 2 | 1 | 2 | 1 | 2 | 1 | 2 | 1 | Check Digit |
Result | 5 | 12 | 5 | 4 | 1 | 16 | 3 | 8 | 7 | |
Digit Sum | 5 | 1+2=3 | 5 | 4 | 1 | 1+6=7 | 3 | 8 | 7 | |
Sum | 5 | 3 | 5 | 4 | 1 | 7 | 3 | 8 | 7 |
The table above has a check digit of 2. The running sum is 43. If we take modulo 10 for validation, 10 – (43 mod 10) is equal to 7. This means this example is not valid. If the calculated result was 2, then the number would have been valid.
Implementation in Go
func Validate(card string) (bool, error) {
card, err := checkCardNumber(card)
if err != nil {
log.Println(err)
return false, err
}
checkDigit, err := strconv.Atoi(card[len(card)-1:])
if err != nil {
log.Fatalln(err)
return false, err
}
calculatedCheckDigit, err := calculateCheckDigit(card)
if err != nil {
log.Println(err)
return false, err
}
return checkDigit == calculatedCheckDigit, nil
}
func checkCardNumber(card string) (string, error) {
pattern := "^(\\d{4}\\s\\d{4}\\s\\d{4}\\s\\d{4}|\\d{16})$"
ok, err := regexp.Match(pattern, []byte(card))
if err != nil {
log.Println(err)
return "", errors.New("unknown error")
}
if !ok {
return "", errors.New("invald card format")
}
return strings.ReplaceAll(card, " ", ""), nil
}
func calculateCheckDigit(card string) (int, error) {
runningSum := 0
for i := (len(card) - 2); i >= 0; i-- {
digit, err := strconv.Atoi(card[i : i+1])
// Not expected error, but just in case
if err != nil {
log.Println(err)
return 0, err
}
if i%2 == 0 {
digit *= 2
for digit > 0 {
runningSum += digit % 10
digit /= 10
}
} else {
runningSum += digit
}
}
return 10 - (runningSum % 10), nil
}
Credit Card Validator API
For simplicity, this project’s API was implemented in the Gin Framework. There is only one endpoint which is a POST request. The reason it is a POST request is that the credit card number is sensitive information and we would not like to have it open for everyone.
The goal is to receive a credit card number and calculate the check digit. We create a structure for this incoming data. Here it is:
type CardRequestBody struct {
CardNumber string `json:"card" binding:"required"`
}
Now we create a router and implement the one endpoint. Then we run on the port we have provided.
router := gin.Default()
router.POST("/verify", func(c *gin.Context) {
var body CardRequestBody
err := c.ShouldBindJSON(&body)
if err != nil {
log.Println("error parsing body ", err)
c.Status(400)
return
}
result, err := validator.Validate(body.CardNumber)
if err != nil {
log.Println("error validating card ", err)
c.JSON(http.StatusOK, gin.H{
"isValid": false,
"message": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{
"isValid": result,
"message": nil,
})
})
router.Run(fmt.Sprintf(":%s", port))
This is a simple way to create an API to validate credit card numbers.
Citation
“Luhn Algorithm.” Wikipedia, Wikimedia Foundation, 16 Mar. 2024, en.wikipedia.org/wiki/Luhn_algorithm.
GfG. “Luhn Algorithm.” GeeksforGeeks, GeeksforGeeks, 19 July 2022, www.geeksforgeeks.org/luhn-algorithm/.
I have even more projects waiting. Interested in Go? Check out my ping project.
Thanks for reading this post!
If you’d like to contact me about anything, send feedback, or want to chat feel free to:
Send an email: andy@serra.us
Archives
Calendar
M | T | W | T | F | S | S |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |