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



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
# File 'lib/ibandit/iban.rb', line 11

def initialize(argument)
  if argument.is_a?(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
  elsif argument.is_a?(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



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

def 
  return @account_number unless country_code == "NZ"

  @account_number[0..6]
end

#account_number_suffixObject



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

def 
  return nil unless country_code == "NZ"

  @account_number[7..-1]
end

#bank_code_passes_checksum_test?Boolean

rubocop:disable Metrics/AbcSize



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

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(:is_invalid) unless mod.zero?

  mod.zero?
end

#bbanObject



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

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

#bsb_check?Boolean



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

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

  valid_modulus_check_branch_code?
end

#local_check_digitsObject



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

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



283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/ibandit/iban.rb', line 283

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



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

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



297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
# File 'lib/ibandit/iban.rb', line 297

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 #



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

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



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

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 #



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

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



261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/ibandit/iban.rb', line 261

def 
  return unless 

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

#valid_account_number_length?Boolean



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

def 
  return unless valid_country_code?

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

  if .length == structure[:account_number_length]
    return true
  end

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

#valid_australian_details?Boolean



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

def valid_australian_details?
  return true unless country_code == "AU"

  bsb_check? && 
end

#valid_bank_code_format?Boolean



237
238
239
240
241
242
243
244
245
246
247
# File 'lib/ibandit/iban.rb', line 237

def valid_bank_code_format?
  return unless valid_bank_code_length?
  return true if structure[:bank_code_length]&.zero?

  if swift_bank_code&.match?(Regexp.new(structure[:bank_code_format]))
    true
  else
    @errors[:bank_code] = Ibandit.translate(:is_invalid)
    false
  end
end

#valid_bank_code_length?Boolean



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

def valid_bank_code_length?
  return unless valid_country_code?
  return true if structure[:bank_code_length]&.zero?

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

  return true if swift_bank_code.length == structure[:bank_code_length]

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

#valid_branch_code_format?Boolean



249
250
251
252
253
254
255
256
257
258
259
# File 'lib/ibandit/iban.rb', line 249

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

  if swift_branch_code&.match?(Regexp.new(structure[:branch_code_format]))
    true
  else
    @errors[:branch_code] = Ibandit.translate(:is_invalid)
    false
  end
end

#valid_branch_code_length?Boolean



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

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

  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



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

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



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

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



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

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



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

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



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

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

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

#valid_length?Boolean



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

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



274
275
276
277
278
279
280
281
# File 'lib/ibandit/iban.rb', line 274

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



365
366
367
368
369
370
# File 'lib/ibandit/iban.rb', line 365

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



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

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



314
315
316
317
318
319
320
321
322
# File 'lib/ibandit/iban.rb', line 314

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



346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
# File 'lib/ibandit/iban.rb', line 346

def valid_swedish_local_details?
  unless Sweden::Validator.valid_clearing_code_length?(branch_code)
    @errors[:branch_code] = Ibandit.translate(:is_invalid)
    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(:is_invalid)
    return false
  end

  true
end

#valid_swedish_swift_details?Boolean



324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
# File 'lib/ibandit/iban.rb', line 324

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(:is_invalid)
    @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(:is_invalid)
    return false
  end

  true
end