Skip to content

Strings: Credit Card Validation #830

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Nov 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@
* [ReverseString](https://github.com/TheAlgorithms/Javascript/blob/master/String/ReverseString.js)
* [ReverseWords](https://github.com/TheAlgorithms/Javascript/blob/master/String/ReverseWords.js)
* [ScrambleStrings](https://github.com/TheAlgorithms/Javascript/blob/master/String/ScrambleStrings.js)
* [ValidateCreditCard](https://github.com/TheAlgorithms/Javascript/blob/master/String/ValidateCreditCard.js)
* [ValidateEmail](https://github.com/TheAlgorithms/Javascript/blob/master/String/ValidateEmail.js)

## Timing-Functions
Expand Down
59 changes: 59 additions & 0 deletions String/ValidateCreditCard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* Validate a given credit card number
*
* The core of the validation of credit card numbers is the Luhn algorithm.
*
* The validation sum should be completely divisible by 10 which is calculated as follows,
* every first digit is added directly to the validation sum.
* For every second digit in the credit card number, the digit is multiplied by 2.
* If the product is greater than 10 the digits of the product are added.
* This resultant digit is considered for the validation sum rather than the digit itself.
*
* Ref: https://www.geeksforgeeks.org/luhn-algorithm/
*/

const luhnValidation = (creditCardNumber) => {
let validationSum = 0
creditCardNumber.split('').forEach((digit, index) => {
let currentDigit = parseInt(digit)
if (index % 2 === 0) {
// Multiply every 2nd digit from the left by 2
currentDigit *= 2
// if product is greater than 10 add the individual digits of the product to get a single digit
if (currentDigit > 9) {
currentDigit %= 10
currentDigit += 1
}
}
validationSum += currentDigit
})

return validationSum % 10 === 0
}

const validateCreditCard = (creditCardString) => {
const validStartSubString = ['4', '5', '6', '37', '34', '35'] // Valid credit card numbers start with these numbers

if (typeof creditCardString !== 'string') {
throw new TypeError('The given value is not a string')
}

const errorMessage = `${creditCardString} is an invalid credit card number because `
if (isNaN(creditCardString)) {
throw new TypeError(errorMessage + 'it has nonnumerical characters.')
}
const creditCardStringLength = creditCardString.length
if (!((creditCardStringLength >= 13) && (creditCardStringLength <= 16))) {
throw new Error(errorMessage + 'of its length.')
}
if (!validStartSubString.some(subString => creditCardString.startsWith(subString))) {
throw new Error(errorMessage + 'of its first two digits.')
}
if (!luhnValidation(creditCardString)) {
throw new Error(errorMessage + 'it fails the Luhn check.')
}

return true
}

export { validateCreditCard }
39 changes: 39 additions & 0 deletions String/test/ValidateCreditCard.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { validateCreditCard } from '../ValidateCreditCard'

describe('Validate credit card number', () => {
it('should throw error if card number is boolean', () => {
const invalidCC = true
expect(() => validateCreditCard(invalidCC)).toThrow(
'The given value is not a string'
)
})
it('returns true if the credit card number is valid', () => {
const validCreditCard = '4111111111111111'
const validationResult = validateCreditCard(validCreditCard)
expect(validationResult).toBe(true)
})
it('should throw an error on non-numeric character in given credit card number', () => {
const nonNumericCCNumbers = ['123ABCDEF', 'ABCDKDKD', 'ADS232']
nonNumericCCNumbers.forEach(nonNumericCC => expect(() => validateCreditCard(nonNumericCC)).toThrow(
`${nonNumericCC} is an invalid credit card number because ` + 'it has nonnumerical characters.'
))
})
it('should throw an error on credit card with invalid length', () => {
const ccWithInvalidLength = ['41111', '4111111111111111111111']
ccWithInvalidLength.forEach(invalidCC => expect(() => validateCreditCard(invalidCC)).toThrow(
`${invalidCC} is an invalid credit card number because ` + 'of its length.'
))
})
it('should throw an error on credit card with invalid start substring', () => {
const ccWithInvalidStartSubstring = ['12345678912345', '23456789123456', '789123456789123', '891234567891234', '912345678912345', '31345678912345', '32345678912345', '33345678912345', '38345678912345']
ccWithInvalidStartSubstring.forEach(invalidCC => expect(() => validateCreditCard(invalidCC)).toThrow(
`${invalidCC} is an invalid credit card number because ` + 'of its first two digits.'
))
})
it('should throw an error on credit card with luhn check fail', () => {
const invalidCCs = ['411111111111111', '371211111111111', '49999999999999']
invalidCCs.forEach(invalidCC => expect(() => validateCreditCard(invalidCC)).toThrow(
`${invalidCC} is an invalid credit card number because ` + 'it fails the Luhn check.'
))
})
})