defmodule Cnum.Validator do @moduledoc false # Validator validates a credit card number through the Luhn algorithm. # Test credit card numbers are valid by default even if they would not pass the Luhn check. require Integer @test_cards [ 2223000048400011, 2223003122003222, 2223520043560014, 30569309025904, 3530111333300000, 3566002020360505, 371449635398431, 378282246310005, 378734493671000, 38520000023237, 4000056655665556, 4012888888881881, 4111111111111111, 4222222222222, 4242424242424242, 5105105105105100, 5200828282828210, 5555555555554444, 5610591081018250, 6011111111111117, 6011000990139424, 6200000000000005 ] @doc false def validate(number) do if Enum.member?(@test_cards, number), do: true, else: luhn_check(number) end # -------------------------------------------------- # defp luhn_check(number) do [ check_digit | remaining_digits ] = Integer.digits(number) |> Enum.reverse luhn_sum(remaining_digits) |> mod_10_check(check_digit) end defp luhn_sum(digits, index \\ 0, acc \\ 0) defp luhn_sum([], _index, acc), do: acc defp luhn_sum([digit | tail], index, acc) when Integer.is_odd(index), do: luhn_sum(tail, index + 1, acc + digit) defp luhn_sum([digit | tail], index, acc) when Integer.is_even(index), do: luhn_sum(tail, index + 1, acc + partial_sum(digit * 2)) defp mod_10_check(digits_sum, check_digit) do Integer.mod((digits_sum + check_digit), 10) == 0 end defp partial_sum(number) when number > 9 do Integer.digits(number) |> Enum.sum() end defp partial_sum(number), do: number end