luhn
This commit is contained in:
commit
06198ced9a
6 changed files with 125 additions and 0 deletions
0
Dockerfile
Normal file
0
Dockerfile
Normal file
0
README.md
Normal file
0
README.md
Normal file
0
cmd/api.go
Normal file
0
cmd/api.go
Normal file
3
go.mod
Normal file
3
go.mod
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module git.tertyshy.dev/cards
|
||||||
|
|
||||||
|
go 1.23.2
|
62
lib/validate.go
Normal file
62
lib/validate.go
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
package validate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Card struct {
|
||||||
|
Pan string
|
||||||
|
Month string
|
||||||
|
Year string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ValidationResult struct {
|
||||||
|
Code int
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
func Luhn(pan string) bool {
|
||||||
|
if pan == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
lastIndex := len(pan) - 1
|
||||||
|
|
||||||
|
asDigits := []int{}
|
||||||
|
|
||||||
|
for _, char := range strings.Split(pan, "") {
|
||||||
|
digit, err := strconv.Atoi(char)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
asDigits = append(asDigits, digit)
|
||||||
|
}
|
||||||
|
|
||||||
|
sum := 0
|
||||||
|
|
||||||
|
// do we really need reversed array?
|
||||||
|
for i, digit := range slices.Backward(asDigits[:lastIndex]) {
|
||||||
|
|
||||||
|
if i%2 == len(pan)%2 {
|
||||||
|
if digit > 4 {
|
||||||
|
sum += 2*digit - 9
|
||||||
|
} else {
|
||||||
|
sum += 2 * digit
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sum += digit
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return asDigits[lastIndex] == (10-(sum%10))%10
|
||||||
|
}
|
||||||
|
|
||||||
|
func Validate(card Card) (bool, ValidationResult) {
|
||||||
|
|
||||||
|
return true, ValidationResult{}
|
||||||
|
}
|
60
lib/validate_test.go
Normal file
60
lib/validate_test.go
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
package validate
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestLuhn(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
pan string
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"Mastercard",
|
||||||
|
"5555555555554444",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Visa",
|
||||||
|
"4111111111111111",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"example from wiki",
|
||||||
|
"17893729974",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"another example",
|
||||||
|
"79927398713",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"worong check digit",
|
||||||
|
"17893729975",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"off by one error",
|
||||||
|
"27893729974",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"empty",
|
||||||
|
"",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"not a digit",
|
||||||
|
"27893a29974",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := Luhn(tt.pan)
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("Luhn(%s) = %v, want %v", tt.name, got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue