defmodule Cnum do @moduledoc """ Cnum provides categorization and verification methods for a credit card number. """ alias Cnum.{Brands, Validator} @doc """ Returns the brand of a given card number. Currently supported brands: - AmericanExpress - DinersClub - Discover - Jcb - Maestro - MasterCard - Visa - VisaElectron ## Parameters - pan: Integer or String that represents the primary account number on a payment card. ## Examples iex> Cnum.type(4111111111111111) "visa" iex> Cnum.type("3400 0000 0000 009") "amex" iex> Cnum.type("1404 0092 0000 0004") "unknown" """ @spec type(any()) :: binary() def type(pan) when is_binary(pan) do first_digits = clean(pan) |> String.slice(0..5) bin_search(first_digits) end def type(pan) do try do Integer.to_string(pan) |> type() rescue _ -> "unknown" end end @doc """ Returns true if the given card number is valid or if the number is a test card number. Test credit card numbers are valid by default. ## Parameters - pan: Integer or String that represents the primary account number on a payment card. ## Examples iex> Cnum.valid?("3400 0000 0000 009") true iex> Cnum.valid?(55555555555544) false iex> Cnum.valid?("") false """ @spec valid?(any()) :: boolean() def valid?(pan) when is_integer(pan) do Validator.validate(pan) end def valid?(pan) do try do String.to_integer(clean(pan)) |> valid?() rescue _ -> false end end # -------------------------------------------------- # defp clean(pan) do Regex.replace(~r/\D/, pan, "") end defp bin_search(""), do: "unknown" defp bin_search(digits) do case Brands.get(digits) do nil -> bin_search(String.slice(digits, 0..-2)) brand -> brand end end end