Skip to content
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

Add structure to allow guessing of BIC based on IBAN #8

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

Rafaeltheraven
Copy link

This is a functionality I need for my own project, but I can imagine it would be useful for others as well so I'm submitting it.

As a side note before I explain my changes, this also adds support for Sudan (SD) and Lybia (LY) to the IBAN list.

Basically, I've gone through all the IBAN regexes and added capturing groups to separately capture what can identify the exact bank the IBAN belongs to. In all cases, this is either a single group for just the bank (either directly as a string or as some numerical code) or two groups in which the first is the bank identifier and the second is the branch identifier.

Because there are significantly more banks than there are countries, I've only added proof-of-concept support for The Netherlands (my country), but the approach should be largely similar. After analyzing the IBAN with the associated regex, the captured groups can be passed along to the calculate_bic method. This method should be specifically implemented per country because every country has their own way they denote bank information in their IBAN. Hopefully, users of this library that want to support their own countries can create patches as needed.

There's a good amount of IBANs to which I did not yet add capturing groups. These are annotated with # No auto-BIC and are unsupported because I could not figure out what country they belong to or that countries particular IBAN structure.

@railsmechanic
Copy link
Owner

Many thanks for your PR. I'll review it soon, but currently I've a lot of work and unfortunately no time.

Copy link
Owner

@railsmechanic railsmechanic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please have a look:

  • The testsfor IBANs from TN and TR failes due invalid regular expressions.
  • The test of the supported_countries/0 function fails, as the number of supported countries is now 118.

But overall, great work.

"TF" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i}, # No auto-BIC
"TG" => %{length: 28, rule: ~r/^[A-Z]{2}[0-9]{22}$/i}, # No auto-BIC
"TL" => %{length: 23, rule: ~r/^([0-9]{3})[0-9]{16}$/i},
"TN" => %{length: 24, rule: ~r/^([0-9]{2})([0-9]{3})[0-9]{13}$/i},
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the entry at https://de.wikipedia.org/wiki/Internationale_Bankkontonummer the regex should be ~r/^([0-9]{2})([0-9]{3})[0-9]{15}$/i

"TG" => %{length: 28, rule: ~r/^[A-Z]{2}[0-9]{22}$/i}, # No auto-BIC
"TL" => %{length: 23, rule: ~r/^([0-9]{3})[0-9]{16}$/i},
"TN" => %{length: 24, rule: ~r/^([0-9]{2})([0-9]{3})[0-9]{13}$/i},
"TR" => %{length: 26, rule: ~r/^([0-9]{5})0[0-9A-Z]{16}$/i},
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rule should be ~r/^([0-9]{5})[0-9][0-9A-Z]{16}$/i

Comment on lines +27 to +32
case Bankster.Iban.validate(iban) do
{:ok, canon_iban} ->
country = Bankster.Iban.country_code(canon_iban)
bban = Bankster.Iban.bban(canon_iban)
regexes = Bankster.Iban.iban_rules()
matches = Regex.run(regexes[country][:rule], bban, [capture: :all_but_first])
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think, could with not be "clearer" here?

with {:ok, canon_iban} <- Bankster.Iban.validate(iban),
     {:ok, country} <- {:ok, Bankster.Iban.country_code(canon_iban)},
     {:ok, bban} <- {:ok, Bankster.Iban.country_code(canon_iban)},
     {:ok, regexes} <- {:ok, Bankster.Iban.iban_rules()},
     {:ok, matches} <- {:ok, Regex.run(regexes[country][:rule], bban, capture: :all_but_first)} do
  calculate_bic(country, matches)
else
  {:error, message} ->
    {:error, message}

  _calculation_failed ->
    {:error, :bic_calculation_failed}
end

end

@spec basic_mapping_nested(%{required(String.t()) => %{required(String.t()) => String.t()}}, String.t(), String.t()) :: {:ok, String.t()} | {:error, :unsupported_bank}
defp basic_mapping_nested(mapping, bank, branch) do
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Compiler complains "function basic_mapping_nested/3 is unused". Is this just for demonstration purpouses?

@@ -309,6 +311,9 @@ defmodule Bankster.Iban do
@spec valid?(String.t()) :: boolean
def valid?(iban), do: match?({:ok, _}, validate(iban))

def iban_rules(), do: @iban_rules
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this only used as a private function? What is the reason for this function?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants