From 06198ced9a80e933ff2b19e95876c94c75589479 Mon Sep 17 00:00:00 2001 From: Greg Date: Fri, 31 Jan 2025 19:28:11 +0200 Subject: [PATCH] luhn --- Dockerfile | 0 README.md | 0 cmd/api.go | 0 go.mod | 3 +++ lib/validate.go | 62 ++++++++++++++++++++++++++++++++++++++++++++ lib/validate_test.go | 60 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 125 insertions(+) create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 cmd/api.go create mode 100644 go.mod create mode 100644 lib/validate.go create mode 100644 lib/validate_test.go diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/cmd/api.go b/cmd/api.go new file mode 100644 index 0000000..e69de29 diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..d13801d --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module git.tertyshy.dev/cards + +go 1.23.2 diff --git a/lib/validate.go b/lib/validate.go new file mode 100644 index 0000000..f2dd883 --- /dev/null +++ b/lib/validate.go @@ -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{} +} diff --git a/lib/validate_test.go b/lib/validate_test.go new file mode 100644 index 0000000..fa8aad6 --- /dev/null +++ b/lib/validate_test.go @@ -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) + } + }) + } +}