nordea-siirto 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/nordea/siirto.rb +23 -0
- data/lib/nordea/siirto/access_token.rb +70 -0
- data/lib/nordea/siirto/errors.rb +13 -0
- data/lib/nordea/siirto/lookup.rb +37 -0
- data/lib/nordea/siirto/pay.rb +102 -0
- data/lib/nordea/siirto/protocols/base.rb +68 -0
- data/lib/nordea/siirto/protocols/curl.rb +66 -0
- data/lib/nordea/siirto/protocols/net_http.rb +52 -0
- data/lib/nordea/siirto/request.rb +8 -0
- data/lib/nordea/siirto/response.rb +8 -0
- data/lib/nordea/siirto/siirto.rb +132 -0
- data/lib/nordea/siirto/version.rb +5 -0
- metadata +57 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 7a303e3afcc99342e66813882aab9976f53b9a8b201cca342226779d0a81bcc9
|
|
4
|
+
data.tar.gz: '0914494f49ad84ab02fd00231c17b91cdb063920a147b57ae000a8093623f31b'
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: c40ad8d7577fe6f02209344a8bf91b509a95cf0cf2543e3c8382fe06451fcba75cbfef69adbb7a88862fc48b6f0d51d12cbae0e390e2c91d53faf7eaf28f099d
|
|
7
|
+
data.tar.gz: fd2662e9219443cd1d4500f082669be73affaa8b1c9dcac61b8ee126326d56d8bdf6a33ed4d415f907a7ebe0f65ad18e67b962b30e3244f31bf4b3d617803713
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
require 'net/http'
|
|
3
|
+
require 'iban'
|
|
4
|
+
require 'active_support'
|
|
5
|
+
require 'active_support/core_ext'
|
|
6
|
+
|
|
7
|
+
# Intended public interface of the gem
|
|
8
|
+
require 'nordea/siirto/siirto.rb'
|
|
9
|
+
|
|
10
|
+
# Implemented requests
|
|
11
|
+
require 'nordea/siirto/access_token.rb'
|
|
12
|
+
require 'nordea/siirto/lookup.rb'
|
|
13
|
+
require 'nordea/siirto/pay.rb'
|
|
14
|
+
|
|
15
|
+
# Implemented protocols
|
|
16
|
+
require 'nordea/siirto/protocols/base.rb'
|
|
17
|
+
require 'nordea/siirto/protocols/net_http.rb'
|
|
18
|
+
require 'nordea/siirto/protocols/curl.rb'
|
|
19
|
+
|
|
20
|
+
# Utility classes
|
|
21
|
+
require 'nordea/siirto/errors.rb'
|
|
22
|
+
require 'nordea/siirto/request.rb'
|
|
23
|
+
require 'nordea/siirto/response.rb'
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
module Nordea
|
|
2
|
+
module Siirto
|
|
3
|
+
# Responsible for fetching access token from the server,
|
|
4
|
+
# and memoizing it.
|
|
5
|
+
module AccessToken
|
|
6
|
+
# Store current access token into REDIS
|
|
7
|
+
KEY = 'Nordea::Siirto::AccessToken'.freeze
|
|
8
|
+
EXPIRATION_BUFFER = 20 # seconds, arbitrary
|
|
9
|
+
MUTEX = Mutex.new
|
|
10
|
+
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
# Fetches access token from server if previous token has expired
|
|
14
|
+
# Memoizes token, and sets expiration time, with some buffer.
|
|
15
|
+
# @return [String]
|
|
16
|
+
# rubocop:disable MethodLength
|
|
17
|
+
def access_token
|
|
18
|
+
# Synchronization is needed, otherwise race condition may ensue:
|
|
19
|
+
# Let's assume token has expired, and two threads ask for a new token.
|
|
20
|
+
# Both proceed to fetch the token from remote server, both return,
|
|
21
|
+
# but the first token is no longer valid.
|
|
22
|
+
MUTEX.synchronize do
|
|
23
|
+
token = Nordea::Siirto.redis.get(KEY)
|
|
24
|
+
return token if token
|
|
25
|
+
|
|
26
|
+
Nordea::Siirto.log('Requesting new access token...')
|
|
27
|
+
payload = response.body
|
|
28
|
+
|
|
29
|
+
token = payload['access_token']
|
|
30
|
+
expires_in = payload['expires_in'] - EXPIRATION_BUFFER
|
|
31
|
+
Nordea::Siirto.redis.set(KEY, token)
|
|
32
|
+
Nordea::Siirto.redis.expire(KEY, expires_in)
|
|
33
|
+
|
|
34
|
+
token
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
# rubocop:enable MethodLength
|
|
38
|
+
|
|
39
|
+
# @return [URI::HTTPS]
|
|
40
|
+
def uri
|
|
41
|
+
@uri ||= URI.parse("#{Nordea::Siirto.endpoint}/auth")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @return Nordea::Siirto::Request
|
|
45
|
+
# rubocop:disable MethodLength
|
|
46
|
+
def request
|
|
47
|
+
request = Nordea::Siirto::Request.new
|
|
48
|
+
request.uri = uri
|
|
49
|
+
request.method = 'POST'
|
|
50
|
+
request.headers = {
|
|
51
|
+
'Accept' => 'application/json',
|
|
52
|
+
'Content-Type' => 'application/x-www-form-urlencoded'
|
|
53
|
+
}
|
|
54
|
+
request.body = {
|
|
55
|
+
grant_type: 'password',
|
|
56
|
+
username: Nordea::Siirto.username,
|
|
57
|
+
password: Nordea::Siirto.api_token,
|
|
58
|
+
client_id: Nordea::Siirto.username
|
|
59
|
+
}.to_query
|
|
60
|
+
request
|
|
61
|
+
end
|
|
62
|
+
# rubocop:enable MethodLength
|
|
63
|
+
|
|
64
|
+
# @return [Nordea::Siirto::Response]
|
|
65
|
+
def response
|
|
66
|
+
Nordea::Siirto.protocol.send!(request)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Nordea
|
|
2
|
+
# Gem specific errors
|
|
3
|
+
module Siirto
|
|
4
|
+
class InitializationError < StandardError; end
|
|
5
|
+
|
|
6
|
+
# Errors used by Pay module
|
|
7
|
+
module Pay
|
|
8
|
+
class InvalidIBAN < ArgumentError; end
|
|
9
|
+
class InvalidPayload < ArgumentError; end
|
|
10
|
+
class MissingLookupId < StandardError; end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module Nordea
|
|
2
|
+
module Siirto
|
|
3
|
+
# Fetches unique LookupId from Nordea server.
|
|
4
|
+
# LookupId is required to make a payment request.
|
|
5
|
+
module Lookup
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
# @return [Hash]
|
|
9
|
+
def lookup
|
|
10
|
+
response = Nordea::Siirto.protocol.send!(request)
|
|
11
|
+
response.body
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# @return [URI::HTTPS]
|
|
15
|
+
def uri
|
|
16
|
+
@uri ||= URI.parse("#{Nordea::Siirto.endpoint}/lookup/uuid")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @return [Nordea::Siirto::Request]
|
|
20
|
+
def request
|
|
21
|
+
request = Nordea::Siirto::Request.new
|
|
22
|
+
request.uri = uri
|
|
23
|
+
request.method = 'GET'
|
|
24
|
+
request.headers = {
|
|
25
|
+
'Accept' => 'application/json',
|
|
26
|
+
'Authorization' => "Bearer #{AccessToken.access_token}"
|
|
27
|
+
}
|
|
28
|
+
request
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @return [Nordea::Siirto::Response]
|
|
32
|
+
def response
|
|
33
|
+
Nordea::Siirto.protocol.send!(request)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
module Nordea
|
|
2
|
+
module Siirto
|
|
3
|
+
# Implements Nordea Siirto IBAN payments.
|
|
4
|
+
module Pay
|
|
5
|
+
# Accepted parameters
|
|
6
|
+
PARAMS = {
|
|
7
|
+
required: [:amount, :currency, :bene_account_number],
|
|
8
|
+
person: [:bene_first_names, :bene_last_name],
|
|
9
|
+
company: [:bene_company_name],
|
|
10
|
+
optional: [:fallback_payment, :reference_number, :payment_message,
|
|
11
|
+
:ultimate_bene_ref_name, :beneficiary_minimum_age,
|
|
12
|
+
:beneficiary_identifier]
|
|
13
|
+
}.freeze
|
|
14
|
+
|
|
15
|
+
module_function
|
|
16
|
+
|
|
17
|
+
# @param [Hash] See README
|
|
18
|
+
# @return [Nordea::Siirto::Response]
|
|
19
|
+
def pay(params) # :nodoc:
|
|
20
|
+
raise InvalidPayload, params.inspect unless valid_payload?(params)
|
|
21
|
+
raise InvalidIBAN, params.inspect unless valid_iban?(params)
|
|
22
|
+
|
|
23
|
+
Nordea::Siirto.protocol.send!(request(params))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @return [URI::HTTPS]
|
|
27
|
+
def uri # :nodoc:
|
|
28
|
+
@uri ||= URI.parse("#{Nordea::Siirto.endpoint}/payment/pay")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @param [Hash]
|
|
32
|
+
# @return [Nordea::Siirto::Request]
|
|
33
|
+
# rubocop:disable MethodLength
|
|
34
|
+
def request(params)
|
|
35
|
+
request = Nordea::Siirto::Request.new
|
|
36
|
+
request.uri = uri
|
|
37
|
+
request.method = 'POST'
|
|
38
|
+
request.headers = {
|
|
39
|
+
'Accept' => 'application/json',
|
|
40
|
+
'Content-type' => 'application/json',
|
|
41
|
+
'Authorization' => "Bearer #{AccessToken.access_token}"
|
|
42
|
+
}
|
|
43
|
+
request.body = format_params(params).to_json
|
|
44
|
+
Nordea::Siirto.log("Body: #{request.body}")
|
|
45
|
+
request
|
|
46
|
+
end
|
|
47
|
+
# rubocop:enable MethodLength
|
|
48
|
+
|
|
49
|
+
# @param [Hash]
|
|
50
|
+
# @return [Hash]
|
|
51
|
+
def format_params(params) # :nodoc:
|
|
52
|
+
hash = params.map do |key, val|
|
|
53
|
+
# dromedar case required
|
|
54
|
+
str = key.to_s.camelize
|
|
55
|
+
str[0] = str[0].downcase
|
|
56
|
+
{ str => val }
|
|
57
|
+
end.reduce(&:merge)
|
|
58
|
+
|
|
59
|
+
# unique lookupId is needed for each payment request
|
|
60
|
+
lookup_id = Lookup.lookup.slice('lookupId')
|
|
61
|
+
raise MissingLookupId unless lookup_id.present?
|
|
62
|
+
|
|
63
|
+
hash.merge(lookup_id)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# @param [Hash]
|
|
68
|
+
# @return [Boolean]
|
|
69
|
+
def valid_iban?(params)
|
|
70
|
+
# It makes sense to check IBAN validity and bank compatibility before
|
|
71
|
+
# sending request
|
|
72
|
+
iban = Iban.new(params[:bene_account_number])
|
|
73
|
+
return false unless iban.validate
|
|
74
|
+
|
|
75
|
+
ALLOWED_BIC.include?(iban.bic)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# @param [Hash]
|
|
79
|
+
# @return [Boolean]
|
|
80
|
+
# rubocop:disable AbcSize,LineLength
|
|
81
|
+
def valid_payload?(params)
|
|
82
|
+
return false unless params.is_a?(Hash)
|
|
83
|
+
|
|
84
|
+
# convenience lambda for testing conditions
|
|
85
|
+
params_present = lambda do |key|
|
|
86
|
+
(params.keys & PARAMS[key]).size == PARAMS[key].size
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# required params present
|
|
90
|
+
return false unless params_present.call(:required)
|
|
91
|
+
|
|
92
|
+
# either person or company params present
|
|
93
|
+
return false if params_present.call(:person) && params_present.call(:company)
|
|
94
|
+
return false unless params_present.call(:person) || params_present.call(:company)
|
|
95
|
+
|
|
96
|
+
# must not contain other params than those listed above
|
|
97
|
+
(params.keys - PARAMS.values.flatten).size.zero?
|
|
98
|
+
end
|
|
99
|
+
# rubocop:enable AbcSize,LineLength
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module Nordea
|
|
2
|
+
module Siirto
|
|
3
|
+
# NOTE: NOT COVERED BY TEST SET
|
|
4
|
+
module Protocols
|
|
5
|
+
# This class may be used as a base class for protocol implementations.
|
|
6
|
+
# Sub-classes should implement :send_request and :parse_response methods
|
|
7
|
+
# correctly.
|
|
8
|
+
#
|
|
9
|
+
# Gem expects protocol implementation to respond to :send! method, which
|
|
10
|
+
# takes in a generic Siirto request object, and returns a generic Siirto
|
|
11
|
+
# response object.
|
|
12
|
+
class Base
|
|
13
|
+
# Public interface of protocol implementations
|
|
14
|
+
# @param [Nordea::Siirto::Request]
|
|
15
|
+
# @return [Nordea::Siirto::Response]
|
|
16
|
+
# rubocop:disable MethodLength,AbcSize,LineLength
|
|
17
|
+
def send!(request)
|
|
18
|
+
uri = request.uri
|
|
19
|
+
Nordea::Siirto.log("Sending request to: #{uri}")
|
|
20
|
+
|
|
21
|
+
# Send request
|
|
22
|
+
begin
|
|
23
|
+
protocol_response = send_request(request)
|
|
24
|
+
rescue StandardError => e
|
|
25
|
+
Nordea::Siirto.log("Failed to send request: #{request.inspect} #{e.message}")
|
|
26
|
+
raise
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Parse response
|
|
30
|
+
begin
|
|
31
|
+
response = parse_response(protocol_response)
|
|
32
|
+
rescue StandardError => e
|
|
33
|
+
Nordea::Siirto.log("Failed to parse response: #{protocol_response.inspect} #{e.message}")
|
|
34
|
+
raise
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Log response
|
|
38
|
+
message = "Server responds: #{response.message}"
|
|
39
|
+
message << " #{response.code}"
|
|
40
|
+
unless response.body['access_token'] # do not log token
|
|
41
|
+
message << " #{response.body}"
|
|
42
|
+
end
|
|
43
|
+
Nordea::Siirto.log(message)
|
|
44
|
+
|
|
45
|
+
response
|
|
46
|
+
end
|
|
47
|
+
# rubocop:enable MethodLength,AbcSize,LineLength
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
# Sub-class must implement
|
|
52
|
+
# @param [Nordea::Siirto::Request]
|
|
53
|
+
# @return [Object] Protocol-specific response object
|
|
54
|
+
def send_request(request)
|
|
55
|
+
raise NotImplementedError, 'Sub-class must implement'
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Sub-class must implement
|
|
59
|
+
# @params [Object] Protocol-specific response object
|
|
60
|
+
# @return [Nordea::Siirto::Response]
|
|
61
|
+
def parse_response(protocol_response)
|
|
62
|
+
raise NotImplementedError, 'Sub-class must implement'
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module Nordea
|
|
2
|
+
module Siirto
|
|
3
|
+
# NOTE: NOT COVERED BY TEST SET
|
|
4
|
+
module Protocols
|
|
5
|
+
# Implements communication with Nordea server using command line cURL.
|
|
6
|
+
# Provided as an alternative protocol for net/http, as some Ruby
|
|
7
|
+
# implementations fail to complete SSL Handshake with Nordea servers.
|
|
8
|
+
# At least JRuby 1.9.17 falls in this category.
|
|
9
|
+
#
|
|
10
|
+
# WARNING: We cannot guarantee what Nordea::Siirto::Response#code
|
|
11
|
+
# will contain. With legacy JRuby, a better way (next to upgrading)
|
|
12
|
+
# would be to wrap sufficiently modern and robust Java HTTP library.
|
|
13
|
+
# That way server responses would be more reliable.
|
|
14
|
+
class Curl < Base
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
# Parses generic Siirto request and returns curl command string
|
|
18
|
+
# @param [Nordea::Siirto::Request]
|
|
19
|
+
# @return [String]
|
|
20
|
+
def create_request(siirto_request)
|
|
21
|
+
# i - show information, not just response body
|
|
22
|
+
# s - hide statusbar, error information
|
|
23
|
+
request = "curl -X #{siirto_request.method} -is"
|
|
24
|
+
siirto_request.headers.each do |header, value|
|
|
25
|
+
request << " --header '#{header}: #{value}'"
|
|
26
|
+
end
|
|
27
|
+
if (body = siirto_request.body).present?
|
|
28
|
+
request << " --data '#{body}'"
|
|
29
|
+
end
|
|
30
|
+
request << " #{siirto_request.uri}"
|
|
31
|
+
request
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Makes the actual request
|
|
35
|
+
# @param [Nordea::Siirto::Request]
|
|
36
|
+
# @return [Net::HTTPRequest]
|
|
37
|
+
def send_request(siirto_request)
|
|
38
|
+
request = create_request(siirto_request)
|
|
39
|
+
IO.popen(request)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Parses curl response string and returns a generic Siirto response
|
|
43
|
+
# @params [String]
|
|
44
|
+
# @return [Nordea::Siirto::Response]
|
|
45
|
+
def parse_response(curl_response)
|
|
46
|
+
lines = curl_response.readlines
|
|
47
|
+
# Absence of network connection
|
|
48
|
+
raise IOError, 'Curl response empty' if lines.blank?
|
|
49
|
+
|
|
50
|
+
code = lines.first.split(' ').last
|
|
51
|
+
body = JSON.parse(lines.last)
|
|
52
|
+
|
|
53
|
+
response = Nordea::Siirto::Response.new
|
|
54
|
+
response.code = code
|
|
55
|
+
# Nordea server responds to curl in HTTP/2, and apparently in HTTP/2
|
|
56
|
+
# there is no standard way to return HTTP status message (e.g. OK, Not
|
|
57
|
+
# Found), unlike in HTTP/1.1. We would need to either force HTTP/1.1
|
|
58
|
+
# or map status codes to messages here.
|
|
59
|
+
response.message = ''
|
|
60
|
+
response.body = body
|
|
61
|
+
response
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Nordea
|
|
2
|
+
module Siirto
|
|
3
|
+
# NOTE: NOT COVERED BY TEST SET
|
|
4
|
+
module Protocols
|
|
5
|
+
# Implements communication with Nordea server using Ruby's standard
|
|
6
|
+
# net/http library
|
|
7
|
+
class NetHttp < Base
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
# Creates protocol-specific request from generic Siirto request
|
|
11
|
+
# @param [Nordea::Siirto::Request]
|
|
12
|
+
# @return [Net::HTTPRequest]
|
|
13
|
+
def create_request(siirto_request)
|
|
14
|
+
# Extract data
|
|
15
|
+
klass = "Net::HTTP::#{siirto_request.method.capitalize}".constantize
|
|
16
|
+
uri = siirto_request.uri.request_uri
|
|
17
|
+
body = siirto_request.body
|
|
18
|
+
headers = siirto_request.headers
|
|
19
|
+
|
|
20
|
+
# Create new Request object
|
|
21
|
+
request = klass.new(uri)
|
|
22
|
+
headers.each do |header, value|
|
|
23
|
+
request[header] = value
|
|
24
|
+
end
|
|
25
|
+
request.body = body if body.present?
|
|
26
|
+
request
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Makes the actual request
|
|
30
|
+
# @param [Nordea::Siirto::Request]
|
|
31
|
+
# @return [Net::HTTPRequest]
|
|
32
|
+
def send_request(siirto_request)
|
|
33
|
+
request = create_request(siirto_request)
|
|
34
|
+
uri = siirto_request.uri
|
|
35
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
36
|
+
http.use_ssl = true if uri.port == 443
|
|
37
|
+
http.request(request)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Parses NET::HTTPResponse into a generic Siirto response
|
|
41
|
+
# @param [Net::HTTPResponse]
|
|
42
|
+
# @return [Nordea::Siirto::Response]
|
|
43
|
+
def parse_response(http_response)
|
|
44
|
+
response = Nordea::Siirto::Response.new
|
|
45
|
+
response.code = http_response.code
|
|
46
|
+
response.body = JSON.parse(http_response.body)
|
|
47
|
+
response.message = http_response.message
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
module Nordea
|
|
2
|
+
# This module is intended as the sole public API of this gem.
|
|
3
|
+
#
|
|
4
|
+
# AccessToken and Lookup requests are needed for actual processing
|
|
5
|
+
# requests, such as Pay, to work. Client should not need to call them
|
|
6
|
+
# directly, and therefore they are not directly callable from this module.
|
|
7
|
+
#
|
|
8
|
+
# A method handle for each request meant to be called directly by client,
|
|
9
|
+
# should be included in this API.
|
|
10
|
+
module Siirto
|
|
11
|
+
# Nordea endpoints
|
|
12
|
+
ENDPOINT = {
|
|
13
|
+
prod: 'https://merchant.mobilewalletservices.nordea.com',
|
|
14
|
+
test: 'https://merchant.trescomas.express'
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
# Only Nordea and OP supported at the moment
|
|
18
|
+
ALLOWED_BIC = %w[NDEAFIHH OKOYFIHH].freeze
|
|
19
|
+
|
|
20
|
+
# Singleton features implemented as such
|
|
21
|
+
class << self
|
|
22
|
+
# Mandatory params: Client must provide these at setup
|
|
23
|
+
attr_reader :server, :username, :api_token
|
|
24
|
+
|
|
25
|
+
# Optional params: Client may provide these at setup
|
|
26
|
+
attr_reader :logger, :tag, :redis, :protocol
|
|
27
|
+
|
|
28
|
+
# Make sure initialization is thread safe
|
|
29
|
+
MUTEX = Mutex.new
|
|
30
|
+
|
|
31
|
+
# Client must initialize Nordea::Siirto module before calling other
|
|
32
|
+
# methods.
|
|
33
|
+
#
|
|
34
|
+
# @params opts [Hash] See README for details
|
|
35
|
+
# @raise [Nordea::Siirto::InitializationError]
|
|
36
|
+
# @return [Boolean]
|
|
37
|
+
# rubocop:disable MethodLength
|
|
38
|
+
def setup(opts)
|
|
39
|
+
MUTEX.synchronize do
|
|
40
|
+
allow_initialize?(opts)
|
|
41
|
+
|
|
42
|
+
# Initialize
|
|
43
|
+
opts.each do |key, val|
|
|
44
|
+
attr = "@#{key}".to_sym
|
|
45
|
+
instance_variable_set(attr, val)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Client can inject logger of choice
|
|
49
|
+
@logger ||= Rails.logger
|
|
50
|
+
|
|
51
|
+
# Client can inject logging tag
|
|
52
|
+
@tag ||= 'Nordea::Siirto --'
|
|
53
|
+
|
|
54
|
+
# Client can inject REDIS instance of choice, or adapter.
|
|
55
|
+
@redis ||= REDIS
|
|
56
|
+
|
|
57
|
+
# Client can inject another Protocol
|
|
58
|
+
@protocol ||= Protocols::NetHttp.new
|
|
59
|
+
|
|
60
|
+
# Initialization complete
|
|
61
|
+
log('Initialized!')
|
|
62
|
+
true
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
# rubocop:enable MethodLength
|
|
66
|
+
|
|
67
|
+
# Checks if gem is already initialized with required parameters.
|
|
68
|
+
# @return [Boolean]
|
|
69
|
+
def initialized?
|
|
70
|
+
server.present? && username.present? && api_token.present?
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# 8.2. Send a payment using IBAN account number
|
|
74
|
+
# POST /payment/pay
|
|
75
|
+
#
|
|
76
|
+
# @param payload [Hash] See README
|
|
77
|
+
# @raise [Nordea::Siirto::Pay::InvalidIBAN]
|
|
78
|
+
# @raise [Nordea::Siirto::Pay::InvalidPayload]
|
|
79
|
+
# @return [Nordea::Siirto::Response]
|
|
80
|
+
def pay(payload)
|
|
81
|
+
Pay.pay(payload)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Convenience method for requests
|
|
85
|
+
# @return [String]
|
|
86
|
+
def endpoint
|
|
87
|
+
ENDPOINT[server]
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Convenience method for requests
|
|
91
|
+
# @param msg [String]
|
|
92
|
+
def log(msg)
|
|
93
|
+
logger.info("#{tag} #{msg}")
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
# Error messages
|
|
99
|
+
ERROR = {
|
|
100
|
+
already_initialized: 'Nordea::Siirto is already initialized.',
|
|
101
|
+
missing_args: 'Invalid or missing arguments. Client must provide
|
|
102
|
+
parameter hash with the following keys: :server (either :prod or
|
|
103
|
+
:test), :username, :api_token.',
|
|
104
|
+
invalid_logger: 'Logger must respond to :info method.',
|
|
105
|
+
invalid_protocol: 'Protocol must respond to :send! method'
|
|
106
|
+
}.freeze
|
|
107
|
+
|
|
108
|
+
# Checks that module has not been previously initialized, and
|
|
109
|
+
# that arguments are more or less acceptable.
|
|
110
|
+
# @raise [Nordea::Siirto::InitializationError]
|
|
111
|
+
# rubocop:disable AbcSize,CyclomaticComplexity,GuardClause
|
|
112
|
+
def allow_initialize?(opts)
|
|
113
|
+
raise InitializationError, ERROR[:already_initialized] if initialized?
|
|
114
|
+
raise InitializationError, ERROR[:missing_args] if missing_args?(opts)
|
|
115
|
+
if opts[:logger] && !opts[:logger].respond_to?(:info)
|
|
116
|
+
raise InitializationError, ERROR[:invalid_logger]
|
|
117
|
+
end
|
|
118
|
+
if opts[:protocol] && !opts[:protocol].respond_to?(:send!)
|
|
119
|
+
raise InitializationError, ERROR[:invalid_protocol]
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
# rubocop:enable AbcSize,CyclomaticComplexity,GuardClause
|
|
123
|
+
|
|
124
|
+
# Checks that required parameters are present.
|
|
125
|
+
# @return [Boolean]
|
|
126
|
+
def missing_args?(opts)
|
|
127
|
+
!(opts[:server] && opts[:username] && opts[:api_token] &&
|
|
128
|
+
ENDPOINT.keys.include?(opts[:server].to_sym))
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: nordea-siirto
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 2.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Matilda Smeds
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2019-09-16 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: |2
|
|
14
|
+
Nordea::Siirto implements requests according to Nordea Siirto protocol,
|
|
15
|
+
which enables real time payments for select Finnish bank accounts
|
|
16
|
+
email: foss@aavasoftware.com
|
|
17
|
+
executables: []
|
|
18
|
+
extensions: []
|
|
19
|
+
extra_rdoc_files: []
|
|
20
|
+
files:
|
|
21
|
+
- lib/nordea/siirto.rb
|
|
22
|
+
- lib/nordea/siirto/access_token.rb
|
|
23
|
+
- lib/nordea/siirto/errors.rb
|
|
24
|
+
- lib/nordea/siirto/lookup.rb
|
|
25
|
+
- lib/nordea/siirto/pay.rb
|
|
26
|
+
- lib/nordea/siirto/protocols/base.rb
|
|
27
|
+
- lib/nordea/siirto/protocols/curl.rb
|
|
28
|
+
- lib/nordea/siirto/protocols/net_http.rb
|
|
29
|
+
- lib/nordea/siirto/request.rb
|
|
30
|
+
- lib/nordea/siirto/response.rb
|
|
31
|
+
- lib/nordea/siirto/siirto.rb
|
|
32
|
+
- lib/nordea/siirto/version.rb
|
|
33
|
+
homepage:
|
|
34
|
+
licenses:
|
|
35
|
+
- MIT
|
|
36
|
+
metadata: {}
|
|
37
|
+
post_install_message:
|
|
38
|
+
rdoc_options: []
|
|
39
|
+
require_paths:
|
|
40
|
+
- lib
|
|
41
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
42
|
+
requirements:
|
|
43
|
+
- - ">="
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '0'
|
|
46
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
47
|
+
requirements:
|
|
48
|
+
- - ">="
|
|
49
|
+
- !ruby/object:Gem::Version
|
|
50
|
+
version: '0'
|
|
51
|
+
requirements: []
|
|
52
|
+
rubyforge_project:
|
|
53
|
+
rubygems_version: 2.7.9
|
|
54
|
+
signing_key:
|
|
55
|
+
specification_version: 4
|
|
56
|
+
summary: Nordea Siirto requests
|
|
57
|
+
test_files: []
|