Class: Ibandit::IBAN

Inherits:
Object
  • Object
show all
Defined in:
lib/ibandit/iban.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(argument) ⇒ IBAN

Returns a new instance of IBAN.



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/ibandit/iban.rb', line 11

def initialize(argument)
  case argument
  when String
    input = argument.to_s.gsub(/\s+/, "").upcase

    pseudo_iban_splitter = PseudoIBANSplitter.new(input)
    is_pseudo_iban_country = Constants::PSEUDO_IBAN_COUNTRY_CODES.
      include?(pseudo_iban_splitter.country_code)
    if pseudo_iban?(input) && is_pseudo_iban_country
      @source = :pseudo_iban
      local_details = pseudo_iban_splitter.split
      build_iban_from_local_details(local_details)
    else
      @source = :iban
      @iban = input
      extract_swift_details_from_iban!
    end
  when Hash
    @source = :local_details
    build_iban_from_local_details(argument)
  else
    raise TypeError, "Must pass an IBAN string or hash of local details"
  end

  @errors = {}
end

Instance Attribute Details

#bank_codeObject (readonly)

Returns the value of attribute bank_code.



7
8
9
# File 'lib/ibandit/iban.rb', line 7

def bank_code
  @bank_code
end

#branch_codeObject (readonly)

Returns the value of attribute branch_code.



7
8
9
# File 'lib/ibandit/iban.rb', line 7

def branch_code
  @branch_code
end

#check_digitsObject (readonly)

Returns the value of attribute check_digits.



7
8
9
# File 'lib/ibandit/iban.rb', line 7

def check_digits
  @check_digits
end

#country_codeObject (readonly)

Returns the value of attribute country_code.



7
8
9
# File 'lib/ibandit/iban.rb', line 7

def country_code
  @country_code
end

#errorsObject (readonly)

Returns the value of attribute errors.



7
8
9
# File 'lib/ibandit/iban.rb', line 7

def errors
  @errors
end

#ibanObject (readonly)

Returns the value of attribute iban.



7
8
9
# File 'lib/ibandit/iban.rb', line 7

def iban
  @iban
end

#sourceObject (readonly)

Returns the value of attribute source.



7
8
9
# File 'lib/ibandit/iban.rb', line 7

def source
  @source
end

#swift_account_numberObject (readonly)

Returns the value of attribute swift_account_number.



7
8
9
# File 'lib/ibandit/iban.rb', line 7

def 
  @swift_account_number
end

#swift_bank_codeObject (readonly)

Returns the value of attribute swift_bank_code.



7
8
9
# File 'lib/ibandit/iban.rb', line 7

def swift_bank_code
  @swift_bank_code
end

#swift_branch_codeObject (readonly)

Returns the value of attribute swift_branch_code.



7
8
9
# File 'lib/ibandit/iban.rb', line 7

def swift_branch_code
  @swift_branch_code
end

Instance Method Details

#account_numberObject



58
59
60
61
62
# File 'lib/ibandit/iban.rb', line 58

def 
  return @account_number unless country_code == "NZ"

  @account_number[0..6]
end

#account_number_suffixObject



64
65
66
67
68
# File 'lib/ibandit/iban.rb', line 64

def 
  return nil unless country_code == "NZ"

  @account_number[7..]
end

#bank_code_passes_checksum_test?Boolean

rubocop:disable Metrics/AbcSize

Returns:

  • (Boolean)


394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
# File 'lib/ibandit/iban.rb', line 394

def bank_code_passes_checksum_test?
  return false unless swift_bank_code
  return false if swift_bank_code.length != 9

  code_digits = swift_bank_code.chars.map(&:to_i)
  mod =
    (
      (3 * (code_digits[0] + code_digits[3] + code_digits[6])) +
      (7 * (code_digits[1] + code_digits[4] + code_digits[7])) +
      (1 * (code_digits[2] + code_digits[5] + code_digits[8]))
    ) % 10

  @errors[:bank_code] = Ibandit.translate(:failed_checksum_test) unless mod.zero?

  mod.zero?
end

#bbanObject



79
80
81
# File 'lib/ibandit/iban.rb', line 79

def bban
  iban[4..] unless iban.nil?
end

#local_check_digitsObject



70
71
72
73
74
75
76
77
# File 'lib/ibandit/iban.rb', line 70

def local_check_digits
  return unless decomposable? && structure[:local_check_digit_position]

  iban.slice(
    structure[:local_check_digit_position] - 1,
    structure[:local_check_digit_length],
  )
end

#passes_country_specific_checks?Boolean

Returns:

  • (Boolean)


290
291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/ibandit/iban.rb', line 290

def passes_country_specific_checks?
  return unless valid_country_code?

  case country_code
  when "DE" then supports_iban_determination?
  when "SE" then valid_swedish_details?
  when "AU" then valid_australian_details?
  when "NZ" then valid_nz_details?
  when "CA" then valid_ca_details?
  when "US" then bank_code_passes_checksum_test?
  else true
  end
end

#pseudo_ibanObject



83
84
85
86
87
88
89
90
# File 'lib/ibandit/iban.rb', line 83

def pseudo_iban
  @pseudo_iban ||= PseudoIBANAssembler.new(
    country_code: country_code,
    bank_code: bank_code,
    branch_code: branch_code,
    account_number: @account_number,
  ).assemble
end

#supports_iban_determination?Boolean

Returns:

  • (Boolean)


304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
# File 'lib/ibandit/iban.rb', line 304

def supports_iban_determination?
  return unless valid_format?
  return true unless country_code == "DE"

  begin
    GermanDetailsConverter.convert(
      country_code: country_code,
      bank_code: swift_bank_code,
      account_number: ,
    )
    true
  rescue UnsupportedAccountDetails
    @errors[:account_number] = Ibandit.translate(:does_not_support_payments)
    false
  end
end

#swift_national_idObject

Component parts #



50
51
52
53
54
55
56
# File 'lib/ibandit/iban.rb', line 50

def swift_national_id
  return if swift_bank_code.nil? && swift_branch_code.nil?

  national_id = swift_bank_code.to_s
  national_id += swift_branch_code.to_s
  national_id.slice(0, structure[:national_id_length])
end

#to_s(format = :compact) ⇒ Object



38
39
40
41
42
43
44
# File 'lib/ibandit/iban.rb', line 38

def to_s(format = :compact)
  case format
  when :compact   then iban.to_s
  when :formatted then formatted
  else raise ArgumentError, "invalid format '#{format}'"
  end
end

#valid?Boolean

Validations #

Returns:

  • (Boolean)


96
97
98
99
100
101
102
103
104
105
# File 'lib/ibandit/iban.rb', line 96

def valid?
  has_iban = !iban.nil?
  has_pseudo_iban = !pseudo_iban.nil?

  if has_pseudo_iban && !has_iban
    valid_pseudo_iban?
  else
    valid_iban?
  end
end

#valid_account_number_format?Boolean

Returns:

  • (Boolean)


268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/ibandit/iban.rb', line 268

def 
  return unless 

  if &.match?(
    entire_string_regex(structure[:account_number_format]),
  )
    true
  else
    @errors[:account_number] = Ibandit.translate(:has_invalid_format)
    false
  end
end

#valid_account_number_length?Boolean

Returns:

  • (Boolean)


195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/ibandit/iban.rb', line 195

def 
  return unless valid_country_code?

  if .nil?
    @errors[:account_number] = Ibandit.translate(:is_required)
    return false
  end

  return true if valid_input_length?(structure[:account_number_length],
                                     .length)

  @errors[:account_number] =
    Ibandit.translate(:wrong_length,
                      expected: structure[:account_number_length])
  false
end

#valid_australian_details?Boolean

Returns:

  • (Boolean)


386
387
388
389
390
391
# File 'lib/ibandit/iban.rb', line 386

def valid_australian_details?
  return true unless country_code == "AU"
  return true unless Ibandit.modulus_checker

  valid_modulus_check_branch_code?
end

#valid_bank_code_format?Boolean

Returns:

  • (Boolean)


238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/ibandit/iban.rb', line 238

def valid_bank_code_format?
  return unless valid_bank_code_length?
  if structure[:bank_code_length].is_a?(Integer) && structure[:bank_code_length].zero?
    return true
  end

  if swift_bank_code&.match?(
    entire_string_regex(structure[:bank_code_format]),
  )
    true
  else
    @errors[:bank_code] = Ibandit.translate(:has_invalid_format)
    false
  end
end

#valid_bank_code_length?Boolean

Returns:

  • (Boolean)


159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/ibandit/iban.rb', line 159

def valid_bank_code_length?
  return unless valid_country_code?
  if structure[:bank_code_length].is_a?(Integer) && structure[:bank_code_length].zero?
    return true
  end

  if swift_bank_code.nil? || swift_bank_code.empty?
    @errors[:bank_code] = Ibandit.translate(:is_required)
    return false
  end

  return true if valid_input_length?(structure[:bank_code_length],
                                     swift_bank_code.length)

  @errors[:bank_code] =
    Ibandit.translate(:wrong_length, expected: structure[:bank_code_length])
  false
end

#valid_branch_code_format?Boolean

Returns:

  • (Boolean)


254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'lib/ibandit/iban.rb', line 254

def valid_branch_code_format?
  return unless valid_branch_code_length?
  return true unless structure[:branch_code_format]

  if swift_branch_code&.match?(
    entire_string_regex(structure[:branch_code_format]),
  )
    true
  else
    @errors[:branch_code] = Ibandit.translate(:has_invalid_format)
    false
  end
end

#valid_branch_code_length?Boolean

Returns:

  • (Boolean)


178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/ibandit/iban.rb', line 178

def valid_branch_code_length?
  return unless valid_country_code?
  return true if swift_branch_code.to_s.length == structure[:branch_code_length]

  if structure[:branch_code_length]&.zero?
    @errors[:branch_code] = Ibandit.translate(:not_used_in_country,
                                              country_code: country_code)
  elsif swift_branch_code.nil? || swift_branch_code.empty?
    @errors[:branch_code] = Ibandit.translate(:is_required)
  else
    @errors[:branch_code] =
      Ibandit.translate(:wrong_length,
                        expected: structure[:branch_code_length])
  end
  false
end

#valid_ca_details?Boolean

Returns:

  • (Boolean)


379
380
381
382
383
384
# File 'lib/ibandit/iban.rb', line 379

def valid_ca_details?
  return true unless country_code == "CA"
  return true unless Ibandit.modulus_checker

  valid_modulus_check_branch_code?
end

#valid_characters?Boolean

Returns:

  • (Boolean)


212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/ibandit/iban.rb', line 212

def valid_characters?
  return if iban.nil?

  if iban.scan(/[^A-Z0-9]/).any?
    @errors[:characters] =
      Ibandit.translate(:non_alphanumeric_characters,
                        characters: iban.scan(/[^A-Z\d]/).join(" "))
    false
  else
    true
  end
end

#valid_check_digits?Boolean

Returns:

  • (Boolean)


130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/ibandit/iban.rb', line 130

def valid_check_digits?
  return unless decomposable? && valid_characters?

  expected_check_digits = CheckDigit.iban(country_code, bban)
  if check_digits == expected_check_digits
    true
  else
    @errors[:check_digits] =
      Ibandit.translate(:invalid_check_digits,
                        expected_check_digits: expected_check_digits,
                        check_digits: check_digits)
    false
  end
end

#valid_country_code?Boolean

Returns:

  • (Boolean)


120
121
122
123
124
125
126
127
128
# File 'lib/ibandit/iban.rb', line 120

def valid_country_code?
  if Ibandit.structures.key?(country_code)
    true
  else
    @errors[:country_code] = Ibandit.translate(:invalid_country_code,
                                               country_code: country_code)
    false
  end
end

#valid_format?Boolean

Returns:

  • (Boolean)


225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/ibandit/iban.rb', line 225

def valid_format?
  return unless valid_country_code?
  return unless structure[:bban_format]

  if bban&.match?(entire_string_regex(structure[:bban_format]))
    true
  else
    @errors[:format] = Ibandit.translate(:invalid_format,
                                         country_code: country_code)
    false
  end
end

#valid_length?Boolean

Returns:

  • (Boolean)


145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/ibandit/iban.rb', line 145

def valid_length?
  return unless valid_country_code? && !iban.nil?

  if iban.length == structure[:total_length]
    true
  else
    @errors[:length] =
      Ibandit.translate(:invalid_length,
                        expected_length: structure[:total_length],
                        length: iban.size)
    false
  end
end

#valid_local_modulus_check?Boolean

Returns:

  • (Boolean)


281
282
283
284
285
286
287
288
# File 'lib/ibandit/iban.rb', line 281

def valid_local_modulus_check?
  return unless valid_format?
  return true unless Ibandit.modulus_checker

  valid_modulus_check_bank_code? &&
    valid_modulus_check_branch_code? &&
    
end

#valid_nz_details?Boolean

Returns:

  • (Boolean)


372
373
374
375
376
377
# File 'lib/ibandit/iban.rb', line 372

def valid_nz_details?
  return true unless country_code == "NZ"
  return true unless Ibandit.modulus_checker

  valid_modulus_check_branch_code?
end

#valid_pseudo_iban_check_digits?Boolean

Returns:

  • (Boolean)


107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/ibandit/iban.rb', line 107

def valid_pseudo_iban_check_digits?
  return true unless source == :pseudo_iban
  return true if check_digits == Constants::PSEUDO_IBAN_CHECK_DIGITS

  @errors[:check_digits] =
    Ibandit.translate(
      :invalid_check_digits,
      expected_check_digits: Constants::PSEUDO_IBAN_CHECK_DIGITS,
      check_digits: check_digits,
    )
  false
end

#valid_swedish_details?Boolean

Returns:

  • (Boolean)


321
322
323
324
325
326
327
328
329
# File 'lib/ibandit/iban.rb', line 321

def valid_swedish_details?
  return true unless country_code == "SE"

  if branch_code
    valid_swedish_local_details?
  else
    valid_swedish_swift_details?
  end
end

#valid_swedish_local_details?Boolean

Returns:

  • (Boolean)


353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
# File 'lib/ibandit/iban.rb', line 353

def valid_swedish_local_details?
  unless Sweden::Validator.valid_clearing_code_length?(branch_code)
    @errors[:branch_code] = Ibandit.translate(:has_invalid_clearing_code_length)
    return false
  end

  valid_serial_number = Sweden::Validator.valid_serial_number_length?(
    clearing_code: branch_code,
    serial_number: ,
  )

  unless valid_serial_number
    @errors[:account_number] = Ibandit.translate(:has_invalid_serial_number)
    return false
  end

  true
end

#valid_swedish_swift_details?Boolean

Returns:

  • (Boolean)


331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
# File 'lib/ibandit/iban.rb', line 331

def valid_swedish_swift_details?
  unless Sweden::Validator.bank_code_exists?(swift_bank_code)
    bank_code_field = bank_code.nil? ? :account_number : :bank_code
    @errors[bank_code_field] = Ibandit.translate(:bank_code_does_not_exist)
    @errors.delete(:bank_code) if bank_code.nil?
    return false
  end

  length_valid =
    Sweden::Validator.(
      bank_code: swift_bank_code,
      account_number: ,
    )

  unless length_valid
    @errors[:account_number] = Ibandit.translate(:has_invalid_length)
    return false
  end

  true
end