Class: Sepa::Client

Inherits:
Object
  • Object
show all
Includes:
ActiveModel::Validations, AttributeChecks, ErrorMessages, Utilities
Defined in:
lib/sepa/client.rb

Overview

Handles parameter validation, key initialization, SoapBuilder initialization, communicating with the bank and Response initialization.

Constant Summary collapse

BANKS =

The list of banks that are currently supported by this gem

%i(
  danske
  nordea
  op
  samlink
).freeze
LANGUAGES =

Languages that are currently supported by the gem

%w(FI SE EN).freeze
ENVIRONMENTS =

Environments that are currently supported by the gem

[:production, :test].freeze
STATUSES =

Statuses that can be given to download file list command. When NEW is given, only those files that have not yet been downloaded will be listed. DOWNLOADED will list only downloaded files and ALL will list every file

%w(NEW DOWNLOADED ALL).freeze

Constants included from ErrorMessages

ErrorMessages::CONTENT_ERROR_MESSAGE, ErrorMessages::CUSTOMER_ID_ERROR_MESSAGE, ErrorMessages::DECRYPTION_ERROR_MESSAGE, ErrorMessages::ENCRYPTION_CERT_ERROR_MESSAGE, ErrorMessages::ENCRYPTION_CERT_REQUEST_ERROR_MESSAGE, ErrorMessages::ENCRYPTION_PRIVATE_KEY_ERROR_MESSAGE, ErrorMessages::ENVIRONMENT_ERROR_MESSAGE, ErrorMessages::FILE_REFERENCE_ERROR_MESSAGE, ErrorMessages::FILE_TYPE_ERROR_MESSAGE, ErrorMessages::HASH_ERROR_MESSAGE, ErrorMessages::NOT_OK_RESPONSE_CODE_ERROR_MESSAGE, ErrorMessages::PIN_ERROR_MESSAGE, ErrorMessages::SIGNATURE_ERROR_MESSAGE, ErrorMessages::SIGNING_CERT_REQUEST_ERROR_MESSAGE, ErrorMessages::STATUS_ERROR_MESSAGE, ErrorMessages::TARGET_ID_ERROR_MESSAGE

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from AttributeChecks

#allowed_commands, #check_command, #check_content, #check_customer_id, #check_encryption_cert_request, #check_encryption_certificate, #check_encryption_private_key, #check_environment, #check_file_reference, #check_file_type, #check_keys, #check_pin, #check_presence_and_length, #check_signing_csr, #check_status, #check_target_id

Methods included from Utilities

#calculate_digest, #canonicalize_exclusively, #canonicalized_node, #cert_request_valid?, #check_validity_against_schema, #csr_to_binary, #decode, #encode, #extract_cert, #format_cert, #format_cert_request, #hmac, #iso_time, #load_body_template, #process_cert_value, #rsa_key, #set_node_id, #validate_signature, #verify_certificate_against_root_certificate, #x509_certificate, #xml_doc

Constructor Details

#initialize(hash = {}) ⇒ Client

Initializes the class. An optional hash of attributes can be given. Environment is set to production if not given, language to 'EN' and status to 'NEW'.

Parameters:

  • hash (Hash) (defaults to: {})

    All the attributes of the client can be given to the construcor in a hash



221
222
223
224
225
226
227
228
229
230
# File 'lib/sepa/client.rb', line 221

def initialize(hash = {})
  attributes(hash)
  self.environment   ||= :production
  self.language      ||= 'EN'
  self.status        ||= 'NEW'
  self.savon_options ||= {
    globals: {},
    locals: {},
  }
end

Instance Attribute Details

#bankSymbol

The bank that is used in this client. One of BANKS.

Returns:

  • (Symbol)


14
15
16
# File 'lib/sepa/client.rb', line 14

def bank
  @bank
end

#bank_encryption_certificateString

Bank's encryption certificate. The request is encrypted with this so that the bank can decrypt the request with their private key. In "pem" format.



156
157
158
# File 'lib/sepa/client.rb', line 156

def bank_encryption_certificate
  @bank_encryption_certificate
end

#commandSymbol

The command that is used with this client. One of AttributeChecks#allowed_commands.

Returns:

  • (Symbol)


19
20
21
# File 'lib/sepa/client.rb', line 19

def command
  @command
end

#contentString

The payload in base64 encoded form. Used with upload file command.

Examples:

Dummy payload

'a2lzc2E='

Returns:

  • (String)


26
27
28
# File 'lib/sepa/client.rb', line 26

def content
  @content
end

#customer_idString

Customer id got from the bank.

Examples:

Nordea's testing customer id

'11111111'

Returns:

  • (String)


33
34
35
# File 'lib/sepa/client.rb', line 33

def customer_id
  @customer_id
end

#encryption_csrString

Encryption certificate signing request. This needs to be generated and is then sent to the bank to be signed.

Returns:

  • (String)

See Also:



163
164
165
# File 'lib/sepa/client.rb', line 163

def encryption_csr
  @encryption_csr
end

#encryption_private_keyString

Own encryption private key. Used to decrypt the response. In "pem" format.



149
150
151
# File 'lib/sepa/client.rb', line 149

def encryption_private_key
  @encryption_private_key
end

#environmentSymbol

The environment to be used. One of ENVIRONMENTS.

Returns:

  • (Symbol)


46
47
48
# File 'lib/sepa/client.rb', line 46

def environment
  @environment
end

#file_referenceString

File reference number used in download_file requests.

Examples:

'11111111A12006030319503000000010'

Returns:

  • (String)


53
54
55
# File 'lib/sepa/client.rb', line 53

def file_reference
  @file_reference
end

#file_typeString

The file type of the file that is about to be uploaded or downloaded. These vary by bank.

Examples:

Nordea's electronic bank statement

'TITO'

Returns:

  • (String)


60
61
62
# File 'lib/sepa/client.rb', line 60

def file_type
  @file_type
end

#languageString

The language to be used in this client. One of LANGUAGES.

Returns:

  • (String)


65
66
67
# File 'lib/sepa/client.rb', line 65

def language
  @language
end

#own_signing_certificateString

Own signing certificate in "pem" format. Embedded in the request

Examples:

Nordea's testing signing certificate

'-----BEGIN CERTIFICATE-----
MIIDwTCCAqmgAwIBAgIEAX1JuTANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJT
RTEeMBwGA1UEChMVTm9yZGVhIEJhbmsgQUIgKHB1YmwpMR8wHQYDVQQDExZOb3Jk
ZWEgQ29ycG9yYXRlIENBIDAxMRQwEgYDVQQFEws1MTY0MDYtMDEyMDAeFw0xMzA1
MDIxMjI2MzRaFw0xNTA1MDIxMjI2MzRaMEQxCzAJBgNVBAYTAkZJMSAwHgYDVQQD
DBdOb3JkZWEgRGVtbyBDZXJ0aWZpY2F0ZTETMBEGA1UEBRMKNTc4MDg2MDIzODCB
nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwtFEfAtbJuGzQwwRumZkvYh2BjGY
VsAMUeiKtOne3bZSeisfCq+TXqL1gI9LofyeAQ9I/sDm6tL80yrD5iaSUqVm6A73
9MsmpW/iyZcVf7ms8xAN51ESUgN6akwZCU9pH62ngJDj2gUsktY0fpsoVsARdrvO
Fk0fTSUXKWd6LbcCAwEAAaOCAR0wggEZMAkGA1UdEwQCMAAwEQYDVR0OBAoECEBw
2cj7+XMAMBMGA1UdIAQMMAowCAYGKoVwRwEDMBMGA1UdIwQMMAqACEALddbbzwun
MDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL29jc3Aubm9yZGVh
LnNlL0NDQTAxMA4GA1UdDwEB/wQEAwIFoDCBhQYDVR0fBH4wfDB6oHigdoZ0bGRh
cCUzQS8vbGRhcC5uYi5zZS9jbiUzRE5vcmRlYStDb3Jwb3JhdGUrQ0ErMDElMkNv
JTNETm9yZGVhK0JhbmsrQUIrJTI4cHVibCUyOSUyQ2MlM0RTRSUzRmNlcnRpZmlj
YXRlcmV2b2NhdGlvbmxpc3QwDQYJKoZIhvcNAQEFBQADggEBACLUPB1Gmq6286/s
ROADo7N+w3eViGJ2fuOTLMy4R0UHOznKZNsuk4zAbS2KycbZsE5py4L8o+IYoaS8
8YHtEeckr2oqHnPpz/0Eg7wItj8Ad+AFWJqzbn6Hu/LQhlnl5JEzXzl3eZj9oiiJ
1q/2CGXvFomY7S4tgpWRmYULtCK6jode0NhgNnAgOI9uy76pSS16aDoiQWUJqQgV
ydowAnqS9h9aQ6gedwbOdtkWmwKMDVXU6aRz9Gvk+JeYJhtpuP3OPNGbbC5L7NVd
no+B6AtwxmG3ozd+mPcMeVuz6kKLAmQyIiBSrRNa5OrTkq/CUzxO9WUgTnm/Sri7
zReR6mU=
  -----END CERTIFICATE-----'

Returns:

  • (String)


127
128
129
# File 'lib/sepa/client.rb', line 127

def own_signing_certificate
  @own_signing_certificate
end

#pinString

The one-time pin got for bank. Used with certificate requests.

Examples:

Danske Bank's testing pin

'1234'

Returns:

  • (String)


77
78
79
# File 'lib/sepa/client.rb', line 77

def pin
  @pin
end

#savon_optionsHash

Options to be passed directly to the underlying savon client. Format is as follows: { globals: { ssl_verify_mode: :none, ..., }, locals: { xml: "", ..., }, }

Returns:

  • (Hash)


178
179
180
# File 'lib/sepa/client.rb', line 178

def savon_options
  @savon_options
end

#signing_csrString

The signing certificate signing request. Used in certificate requests.

Examples:

'-----BEGIN CERTIFICATE REQUEST-----
MIIBczCB3QIBADA0MRIwEAYDVQQDEwlEZXZsYWIgT3kxETAPBgNVBAUTCDExMTEx
MTExMQswCQYDVQQGEwJGSTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAo9wU
c2Ys5hSso4nEanbc+RIhL71aS6GBGiWAegXjhlyb6dpwigrZBFPw4u6UZV/Vq7Y7
Ku3uBq5rfZwk+lA+c/B634Eu0zWdI+EYfQxKVRrBrmhiGplKEtglHXbNmmMOn07e
LPUaB0Ipx/6h/UczJGBINdtcuIbYVu0r7ZfyWbUCAwEAAaAAMA0GCSqGSIb3DQEB
BQUAA4GBAIhh2o8mN4Byn+w1jdbhq6lxEXYqdqdh1F6GCajt2lQMUBgYP23I5cS/
Z+SYNhu8vbj52cGQPAwEDN6mm5yLpcXu40wYzgWyfStLXV9d/b4hMy9qLMW00Dzb
jo2ekdSDdw8qxKyxj1piv8oYzMd4fCjCpL+WDZtq7mdLErVZ92gH
-----END CERTIFICATE REQUEST-----'

Returns:

  • (String)


143
144
145
# File 'lib/sepa/client.rb', line 143

def signing_csr
  @signing_csr
end

#signing_private_keyString

Signing private key which is used to sign the request

Examples:

Nordea's testing private key

'-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDC0UR8C1sm4bNDDBG6ZmS9iHYGMZhWwAxR6Iq06d7dtlJ6Kx8K
r5NeovWAj0uh/J4BD0j+wObq0vzTKsPmJpJSpWboDvf0yyalb+LJlxV/uazzEA3n
URJSA3pqTBkJT2kfraeAkOPaBSyS1jR+myhWwBF2u84WTR9NJRcpZ3ottwIDAQAB
AoGBAKrfddv+8eI2kE68ZUhCyxVafXqNQXrFU4j8F7z6bBm28rxo2f87ZFzbPc2W
4dWghs2TJIkdlOxeRpbIqa5SIn+HBel8+6wo2gLO4g0bfT44Y1bqjRkdiPlSCJW0
PV1hSd5SRVt7+0yGfCWy559Fzhc/mQQUkhkytc0zYeEwULYxAkEA3uTN7rvZuEcE
sPUehmg8PyBUGYK9KFkr9FiI0cL8FpxZ0l9pW5DQI7pT9HWhrJp+78SKamcT8cHK
1OMBakxeXQJBAN/A52wpt2H6IM8Cxza3toQZhqo1mq4bcarUWq65IJ5jnfFtGdR2
9XUh65YlElUqyDWyuWXRFdeUabu1Qznj8yMCQDzLJUvvGpQDcskdIiVAuuXw2F9Y
5GTj5XQwzaiAyScVn/4cHe1mkw6bnJh5mQ4t2V9mOOaKlMsEs2DbRaCLkdUCQGWF
Gbsqpkiu+0nRgd+itQ30ovQBREAwtX8DwG08E7+phRTwImMS4kWV8VT7VvkLYzFx
+MpodleMv/hpwqm2ci8CQQCUEgwDBEp+FM+2Y5N1KwSGzGBL9LtpnAsqcLG9JxhO
f4Mwz4xhPXMVlvq1wESLPrDUFQpZ4eOZ4XX2MTo4GH39
-----END RSA PRIVATE KEY-----'

Returns:

  • (String)


98
99
100
# File 'lib/sepa/client.rb', line 98

def signing_private_key
  @signing_private_key
end

#statusString

Used to filter files in download_file_list request. One of STATUSES.

Returns:

  • (String)


70
71
72
# File 'lib/sepa/client.rb', line 70

def status
  @status
end

#target_idString

A file categorization id used by Nordea. Can be retrieved with get_user_info request. Not used with Danske Bank

Examples:

Nordea's testing target id

'11111111A1'

Returns:

  • (String)


41
42
43
# File 'lib/sepa/client.rb', line 41

def target_id
  @target_id
end

Instance Method Details

#attributes(hash) ⇒ Object

Sets the attributes given in a hash

Examples:

{
  bank: :nordea,
  command: :download_file_list
}

Parameters:

  • hash (Hash)

    Hash of parameters



254
255
256
257
258
# File 'lib/sepa/client.rb', line 254

def attributes(hash)
  hash.each do |name, value|
    send("#{name}=", value)
  end
end

#create_hashHash (private)

Creates a hash of all instance variables and their values. Before the actual hash is created, the #signing_private_key is converted to OpenSSL::PKey::RSA using #initialize_signing_private_key method.

Returns:

  • (Hash)

    All instance variables in a hash with their names as symbols as keys



293
294
295
296
297
298
299
300
301
302
303
304
305
306
# File 'lib/sepa/client.rb', line 293

def create_hash
  initialize_signing_private_key
  iv = {}

  # Create hash of all instance variables
  instance_variables.map do |name|
    key = name[1..-1].to_sym
    value = instance_variable_get(name)

    iv[key] = value
  end

  iv
end

#initialize_response(error, response) ⇒ Response (private)

Initializes Response as correct class for a bank. Also converts possible #encryption_private_key from String to OpenSSL::PKey::RSA.

Parameters:

  • error (String)

    Possible error got from #send_request

  • response (String)

    A soap response in plain xml

Returns:



335
336
337
338
339
340
341
342
343
344
345
346
347
# File 'lib/sepa/client.rb', line 335

def initialize_response(error, response)
  options = {
    command:     command,
    environment: environment,
    error:       error,
    response:    response,
  }
  if encryption_private_key && !encryption_private_key.empty?
    options[:encryption_private_key] = rsa_key(encryption_private_key)
  end

  "Sepa::#{bank.capitalize}Response".constantize.new(options)
end

#initialize_signing_private_keyOpenSSL::PKey::RSA (private)

Converts the #signing_private_key from String to OpenSSL::PKey::RSA

Returns:

  • (OpenSSL::PKey::RSA)


310
311
312
# File 'lib/sepa/client.rb', line 310

def initialize_signing_private_key
  @signing_private_key = rsa_key(@signing_private_key) if @signing_private_key
end

#savon_globalsObject (private)



357
358
359
# File 'lib/sepa/client.rb', line 357

def savon_globals
  { wsdl: wsdl }.merge(savon_options[:globals] || {})
end

#savon_localsObject (private)



361
362
363
# File 'lib/sepa/client.rb', line 361

def savon_locals
  { xml: SoapBuilder.new(create_hash).to_xml }.merge(savon_options[:locals] || {})
end

#send_requestResponse

Sends request to the bank specified in the attributes. First a new SoapBuilder class is initialized with a hash of the parameters given to the client with the #create_hash method. After this, a Savon client is initialized with a WSDL file got from #wsdl. After this, the Savon client makes the actual call to the server with the #command and the constructed SoapBuilder. After the call, the xml is extracted from the Savon response and the response is then checked for any Savon::Error errors. After this a Response is initialized using the #initialize_response method with the xml response and possible errors.

Returns:

Raises:

  • (ArgumentError)

    if some of the parameters are not valid



269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/sepa/client.rb', line 269

def send_request
  raise ArgumentError, errors.messages unless valid?

  client = Savon.client(savon_globals)

  begin
    error = nil
    response = client.call(soap_command, savon_locals)
    response &&= response.to_xml
  rescue Savon::Error => e
    response = nil
    error = e.http.body
  end

  initialize_response(error, response)
end

#soap_commandObject (private)



349
350
351
352
353
354
355
# File 'lib/sepa/client.rb', line 349

def soap_command
  if @command == :renew_certificate && [:nordea, :op, :samlink].include?(@bank)
    :get_certificate
  else
    @command
  end
end

#wsdlString (private)

Returns path to WSDL file according to #bank and #command

Returns:

  • (String)

    Path to the WSDL file of the bank and command



316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/sepa/client.rb', line 316

def wsdl
  file = if STANDARD_COMMANDS.include?(command)
           "wsdl_#{bank}"
         else
           "wsdl_#{bank}_cert"
         end

  path  = "#{WSDL_PATH}/#{file}"
  path2 = "#{path}_#{environment}.xml"

  File.exist?(path2) ? path2 : "#{path}.xml"
end