Skip to content

Commit

Permalink
Credit card validation (#924)
Browse files Browse the repository at this point in the history
  • Loading branch information
alessmar committed May 1, 2022
1 parent bb30072 commit dd2857a
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 2 deletions.
7 changes: 5 additions & 2 deletions .github/workflows/workflow.yml
Expand Up @@ -41,8 +41,11 @@ jobs:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v3
with:
go-version: 1.16
- uses: actions/checkout@v3
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
uses: golangci/golangci-lint-action@v3
with:
version: v1.45.2
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -153,6 +153,7 @@ Baked-in Validations
| bcp47_language_tag | Language tag (BCP 47) |
| btc_addr | Bitcoin Address |
| btc_addr_bech32 | Bitcoin Bech32 Address (segwit) |
| credit_card | Credit Card Number |
| datetime | Datetime |
| e164 | e164 formatted phone number |
| email | E-mail String
Expand Down
39 changes: 39 additions & 0 deletions baked_in.go
Expand Up @@ -203,6 +203,7 @@ var (
"bic": isIsoBicFormat,
"semver": isSemverFormat,
"dns_rfc1035_label": isDnsRFC1035LabelFormat,
"credit_card": isCreditCard,
}
)

Expand Down Expand Up @@ -2469,3 +2470,41 @@ func isDnsRFC1035LabelFormat(fl FieldLevel) bool {
val := fl.Field().String()
return dnsRegexRFC1035Label.MatchString(val)
}

// isCreditCard is the validation function for validating if the current field's value is a valid credit card number
func isCreditCard(fl FieldLevel) bool {
val := fl.Field().String()
var creditCard bytes.Buffer
segments := strings.Split(val, " ")
for _, segment := range segments {
if len(segment) < 3 {
return false
}
creditCard.WriteString(segment)
}

ccDigits := strings.Split(creditCard.String(), "")
size := len(ccDigits)
if size < 12 || size > 19 {
return false
}

sum := 0
for i, digit := range ccDigits {
value, err := strconv.Atoi(digit)
if err != nil {
return false
}
if size%2 == 0 && i%2 == 0 || size%2 == 1 && i%2 == 1 {
v := value * 2
if v >= 10 {
sum += 1 + (v % 10)
} else {
sum += v
}
} else {
sum += value
}
}
return (sum % 10) == 0
}
6 changes: 6 additions & 0 deletions doc.go
Expand Up @@ -1317,6 +1317,12 @@ More information on https://semver.org/
Usage: semver
Credit Card
This validates that a string value contains a valid credit card number using Luhn algoritm.
Usage: credit_card
Alias Validators and Tags
NOTE: When returning an error, the tag returned in "FieldError" will be
Expand Down
38 changes: 38 additions & 0 deletions validator_test.go
Expand Up @@ -11750,3 +11750,41 @@ func TestPostCodeByIso3166Alpha2Field_InvalidKind(t *testing.T) {
_ = New().Struct(test{"ABC", 123, false})
t.Errorf("Didn't panic as expected")
}

func TestCreditCardFormatValidation(t *testing.T) {
tests := []struct {
value string `validate:"credit_card"`
tag string
expected bool
}{
{"586824160825533338", "credit_card", true},
{"586824160825533328", "credit_card", false},
{"4624748233249780", "credit_card", true},
{"4624748233349780", "credit_card", false},
{"378282246310005", "credit_card", true},
{"378282146310005", "credit_card", false},
{"4624 7482 3324 9780", "credit_card", true},
{"4624 7482 3324 9780", "credit_card", false},
}

validate := New()

for i, test := range tests {
errs := validate.Var(test.value, test.tag)

if test.expected {
if !IsEqual(errs, nil) {
t.Fatalf("Index: %d credit_card failed Error: %s", i, errs)
}
} else {
if IsEqual(errs, nil) {
t.Fatalf("Index: %d credit_card failed Error: %s", i, errs)
} else {
val := getError(errs, "", "")
if val.Tag() != "credit_card" {
t.Fatalf("Index: %d credit_card failed Error: %s", i, errs)
}
}
}
}
}

0 comments on commit dd2857a

Please sign in to comment.