afterbanks-psd2-api-ruby 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 74a77be49119cc3b5c78ebcdb319db8f2fb0dc240d9c7374c5a65bfeee4086ca
4
+ data.tar.gz: 75d2475c8af58adfb6cf21954057a7076f8367da1b31f9dc0315fa0e05e79d72
5
+ SHA512:
6
+ metadata.gz: f8978cabe120ef0cdfc65618af4515587f2120c6c0272e0deeffee291f1c0f87a3b25e78079bdc8075594205cc408c83485372361895178596a94f2d6c058cd1
7
+ data.tar.gz: 6b01263e08c321231c7e93111e1752ab9dc4e20b36d45018f35238a59c9bd0cfb90a04ee59160abbd3b531b12b3857010c2dd807d9bf1d103c58c1b162fb56de
@@ -0,0 +1,44 @@
1
+ version: 2.1
2
+
3
+ executors:
4
+ gem-executor:
5
+ working_directory: ~/tmp/afterbanks-psd2-api-ruby
6
+ docker:
7
+ - image: cimg/ruby:3.0.6
8
+ environment:
9
+ TZ: "Europe/Madrid"
10
+
11
+ jobs:
12
+ build-test-gem:
13
+ executor: gem-executor
14
+ steps:
15
+ - checkout
16
+
17
+ - run:
18
+ name: Update bundler
19
+ command: gem install bundler -v 2.2.5
20
+
21
+ - restore_cache:
22
+ keys:
23
+ - afterbanks-psd2-api-ruby-bundle-v2-{{ checksum "Gemfile.lock" }}
24
+ - afterbanks-psd2-api-ruby-bundle-v2-
25
+
26
+ - run: bundle install
27
+
28
+ - save_cache:
29
+ key: afterbanks-psd2-api-ruby-bundle-v2-{{ checksum "Gemfile.lock" }}
30
+ paths:
31
+ - vendor/bundle
32
+
33
+ - run:
34
+ name: Run rspec
35
+ command: bundle exec rspec spec/
36
+
37
+ - run:
38
+ name: Run Rubocop
39
+ command: bundle exec rubocop
40
+
41
+ workflows:
42
+ build-test-gem:
43
+ jobs:
44
+ - build-test-gem
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ pkg/*
2
+ .byebug_history
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,75 @@
1
+ inherit_from: .rubocop_exclusions.yml
2
+
3
+ AllCops:
4
+ DisabledByDefault: true
5
+ SuggestExtensions: false
6
+ TargetRubyVersion: 2.6.6
7
+
8
+ Documentation:
9
+ Enabled: false
10
+
11
+ # Enable Bundler department
12
+ Bundler:
13
+ Enabled: true
14
+
15
+ # Enable lint department
16
+ Lint:
17
+ Enabled: true
18
+
19
+ # Enabling layout department
20
+ Layout:
21
+ Enabled: true
22
+
23
+ # Enabling Security department
24
+ Security:
25
+ Enabled: true
26
+
27
+ Layout/MultilineMethodCallIndentation:
28
+ Enabled: true
29
+ EnforcedStyle: indented_relative_to_receiver
30
+
31
+ Style/CollectionMethods:
32
+ Enabled: true
33
+ PreferredMethods:
34
+ # inject seems more common in the community.
35
+ inject: "reduce"
36
+ collect: "map"
37
+
38
+ Lint/BooleanSymbol:
39
+ Exclude:
40
+ - app/uploaders/attachment_uploader.rb
41
+
42
+ Layout/BlockAlignment:
43
+ EnforcedStyleAlignWith: start_of_block
44
+
45
+ Layout/HashAlignment:
46
+ AllowMultipleStyles: true
47
+ EnforcedHashRocketStyle: table
48
+ EnforcedColonStyle: table
49
+
50
+ Layout/SpaceBeforeBrackets: # (new in 1.7)
51
+ Enabled: true
52
+ Lint/AmbiguousAssignment: # (new in 1.7)
53
+ Enabled: true
54
+ Lint/DeprecatedConstants: # (new in 1.8)
55
+ Enabled: true
56
+ Lint/DuplicateBranch: # (new in 1.3)
57
+ Enabled: true
58
+ Lint/DuplicateRegexpCharacterClassElement: # (new in 1.1)
59
+ Enabled: true
60
+ Lint/EmptyBlock: # (new in 1.1)
61
+ Enabled: true
62
+ Lint/EmptyClass: # (new in 1.3)
63
+ Enabled: true
64
+ Lint/LambdaWithoutLiteralBlock: # (new in 1.8)
65
+ Enabled: true
66
+ Lint/NoReturnInBeginEndBlocks: # (new in 1.2)
67
+ Enabled: true
68
+ Lint/RedundantDirGlobSort: # (new in 1.8)
69
+ Enabled: true
70
+ Lint/ToEnumArguments: # (new in 1.1)
71
+ Enabled: true
72
+ Lint/UnexpectedBlockArity: # (new in 1.5)
73
+ Enabled: true
74
+ Lint/UnmodifiedReduceAccumulator: # (new in 1.1)
75
+ Enabled: true
@@ -0,0 +1,10 @@
1
+ # Offense count: 1
2
+ Lint/MissingSuper:
3
+ Exclude:
4
+ - 'lib/afterbanks_psd2/collection.rb'
5
+
6
+ # Offense count: 2
7
+ Security/MarshalLoad:
8
+ Exclude:
9
+ - 'spec/afterbanks_psd2/resources/bank_spec.rb'
10
+ - 'spec/afterbanks_psd2/resources/user_spec.rb'
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in afterbanks.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,90 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ afterbanks-psd2-api-ruby (0.1.0)
5
+ rest-client (~> 2.0.2)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ addressable (2.7.0)
11
+ public_suffix (>= 2.0.2, < 5.0)
12
+ ast (2.4.1)
13
+ crack (0.4.3)
14
+ safe_yaml (~> 1.0.0)
15
+ diff-lcs (1.3)
16
+ domain_name (0.5.20190701)
17
+ unf (>= 0.0.5, < 1.0.0)
18
+ hashdiff (1.0.1)
19
+ http-cookie (1.0.3)
20
+ domain_name (~> 0.5)
21
+ mime-types (3.3.1)
22
+ mime-types-data (~> 3.2015)
23
+ mime-types-data (3.2020.1104)
24
+ netrc (0.11.0)
25
+ parallel (1.19.1)
26
+ parser (3.0.0.0)
27
+ ast (~> 2.4.1)
28
+ public_suffix (4.0.5)
29
+ rainbow (3.0.0)
30
+ rake (13.0.3)
31
+ regexp_parser (2.0.3)
32
+ rest-client (2.0.2)
33
+ http-cookie (>= 1.0.2, < 2.0)
34
+ mime-types (>= 1.16, < 4.0)
35
+ netrc (~> 0.8)
36
+ rexml (3.1.9)
37
+ rspec (3.7.0)
38
+ rspec-core (~> 3.7.0)
39
+ rspec-expectations (~> 3.7.0)
40
+ rspec-mocks (~> 3.7.0)
41
+ rspec-core (3.7.0)
42
+ rspec-support (~> 3.7.0)
43
+ rspec-expectations (3.7.0)
44
+ diff-lcs (>= 1.2.0, < 2.0)
45
+ rspec-support (~> 3.7.0)
46
+ rspec-mocks (3.7.0)
47
+ diff-lcs (>= 1.2.0, < 2.0)
48
+ rspec-support (~> 3.7.0)
49
+ rspec-support (3.7.0)
50
+ rubocop (1.8.1)
51
+ parallel (~> 1.10)
52
+ parser (>= 3.0.0.0)
53
+ rainbow (>= 2.2.2, < 4.0)
54
+ regexp_parser (>= 1.8, < 3.0)
55
+ rexml
56
+ rubocop-ast (>= 1.2.0, < 2.0)
57
+ ruby-progressbar (~> 1.7)
58
+ unicode-display_width (>= 1.4.0, < 3.0)
59
+ rubocop-ast (1.4.0)
60
+ parser (>= 2.7.1.5)
61
+ rubocop-rspec (2.1.0)
62
+ rubocop (~> 1.0)
63
+ rubocop-ast (>= 1.1.0)
64
+ ruby-progressbar (1.10.1)
65
+ safe_yaml (1.0.5)
66
+ timecop (0.9.2)
67
+ unf (0.1.4)
68
+ unf_ext
69
+ unf_ext (0.0.7.7)
70
+ unicode-display_width (1.6.0)
71
+ webmock (3.8.3)
72
+ addressable (>= 2.3.6)
73
+ crack (>= 0.3.2)
74
+ hashdiff (>= 0.4.0, < 2.0.0)
75
+
76
+ PLATFORMS
77
+ ruby
78
+
79
+ DEPENDENCIES
80
+ afterbanks-psd2-api-ruby!
81
+ bundler
82
+ rake
83
+ rspec
84
+ rubocop
85
+ rubocop-rspec
86
+ timecop
87
+ webmock
88
+
89
+ BUNDLED WITH
90
+ 2.1.4
data/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # Ruby client for the Afterbanks PSD2 API
2
+
3
+ [![CircleCI](https://circleci.com/gh/quipuapp/afterbanks-psd2-api-ruby.svg?style=shield)](https://circleci.com/gh/quipuapp/afterbanks-psd2-api-ruby)
4
+
5
+ This is a Ruby client for the Afterbanks' PSD2 API
6
+
7
+ Installation
8
+ ---------
9
+
10
+ Install the gem (`gem install afterbanks-psd2-api-ruby`) or include it in your Gemfile and `bundle`.
11
+
12
+ Configuration
13
+ ---------
14
+
15
+ Just set the service key by doing this:
16
+
17
+ ```ruby
18
+ AfterbanksPSD2.servicekey = 'yourservicekey'
19
+ ```
20
+
21
+ Or, if you use it in a Rails application, create an initializer with this content:
22
+
23
+ ```ruby
24
+ require 'afterbanks_psd2'
25
+
26
+ AfterbanksPSD2.configure do |config|
27
+ config.servicekey = 'yourservicekey'
28
+ end
29
+ ```
30
+
31
+ You can set a `logger` as well.
32
+
33
+ Changelog
34
+ ---------
35
+
36
+ * 0.1.0 First full version, including resource wrapping (banks, user, accounts, transactions), error coverage and support for logging
37
+
38
+ TODO
39
+ ----
40
+
41
+ * Full usage for each resource
42
+ * Proper explanation of the `AfterbanksPSD2:Error` and its subclasses
43
+
44
+ List of Error
45
+ ------------
46
+
47
+ * `1` -> GenericError
48
+ * `50` -> IncorrectParametersError (When call API)
49
+ * `C000` -> GenericConsentError
50
+ * `C001` -> InvalidConsentError
51
+ * `C002` -> ConsentWithUnfinalizedProcessError
52
+ * `C003` -> ProductMismatchConsentError
53
+ * `C004` -> ExpiredConsentError
54
+ * `C005` -> MaximumNumberOfCallsReachedConsentError
55
+ * `T000` -> GenericTransactionError
56
+ * `T001` -> InvalidConsentForProductError
57
+
58
+
59
+
60
+ About Afterbanks
61
+ ------------
62
+
63
+ * [Public site](https://www.afterbanks.com)
64
+ * [Documentation](https://app.swaggerhub.com/apis/Afterbanks/Afterbanks-PSD2-ES)
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core'
5
+ require 'rspec/core/rake_task'
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'afterbanks_psd2/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'afterbanks-psd2-api-ruby'
9
+ spec.version = AfterbanksPSD2::VERSION
10
+ spec.authors = ['Florent Lemaitre']
11
+ spec.email = ['flemaitre@sellsy.com']
12
+
13
+ spec.summary = "Ruby client for the Afterbanks' PSD2 API"
14
+ spec.homepage = 'https://getquipu.com'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = 'exe'
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.add_dependency 'rest-client', '~> 2.0.2'
24
+
25
+ spec.add_development_dependency 'bundler'
26
+ spec.add_development_dependency 'rake'
27
+ spec.add_development_dependency 'rspec'
28
+ spec.add_development_dependency 'rubocop'
29
+ spec.add_development_dependency 'rubocop-rspec'
30
+ spec.add_development_dependency 'timecop'
31
+ spec.add_development_dependency 'webmock'
32
+ end
@@ -0,0 +1,133 @@
1
+ require 'rest-client'
2
+ require 'json'
3
+
4
+ module AfterbanksPSD2
5
+ class << self
6
+ def configuration
7
+ @configuration ||= Configuration.new
8
+ end
9
+
10
+ def configure
11
+ yield(configuration)
12
+ end
13
+
14
+ def api_call(method:, path:, params: {}, options: {})
15
+ api_url = ENV['AFTERBANKS_API_URL'] || 'https://apipsd2.afterbanks.com'
16
+ url = api_url + path
17
+
18
+ request_params = { method: method, url: url }
19
+
20
+ if options[:timeout]
21
+ request_params.merge!(timeout: options[:timeout])
22
+ end
23
+
24
+ if method == :post
25
+ request_params.merge!(payload: params)
26
+ else
27
+ request_params.merge!(headers: { params: params })
28
+ end
29
+
30
+ response = begin
31
+ RestClient::Request.execute(**request_params)
32
+ rescue RestClient::BadRequest, RestClient::ExpectationFailed => bad_request
33
+ # Check
34
+ bad_request.response
35
+ rescue RestClient::Exceptions::ReadTimeout
36
+ log_request(
37
+ method: method,
38
+ url: url,
39
+ params: params
40
+ )
41
+
42
+ raise
43
+ end
44
+
45
+ debug_id = response.headers[:debug_id]
46
+
47
+ log_request(
48
+ method: method,
49
+ url: url,
50
+ params: params,
51
+ debug_id: debug_id
52
+ )
53
+
54
+ response_body = JSON.parse(response.body)
55
+
56
+ treat_errors_if_any(
57
+ response_body: response_body,
58
+ debug_id: debug_id
59
+ )
60
+
61
+ [response_body, debug_id]
62
+ end
63
+
64
+ def log_request(method:, url:, params: {}, debug_id: nil)
65
+ logger = AfterbanksPSD2.configuration.logger
66
+ return if logger.nil?
67
+
68
+ now = Time.now
69
+
70
+ safe_params = {}
71
+ params.each do |key, value|
72
+ safe_value = if %w{servicekey}.include?(key.to_s)
73
+ "<masked>"
74
+ else
75
+ value
76
+ end
77
+
78
+ safe_value = safe_value.to_s if safe_value.is_a?(Symbol)
79
+
80
+ safe_params[key] = safe_value
81
+ end
82
+
83
+ logger.info(
84
+ message: 'Afterbanks PSD2 request',
85
+ method: method.upcase.to_s,
86
+ url: url,
87
+ time: now.to_s,
88
+ timestamp: now.to_i,
89
+ debug_id: debug_id || 'none',
90
+ params: safe_params
91
+ )
92
+ end
93
+
94
+ def treat_errors_if_any(response_body:, debug_id:)
95
+ return unless response_body.is_a?(Hash)
96
+
97
+ code = response_body['code']
98
+ integer_code = code.to_i
99
+ if integer_code.to_s == code
100
+ code = integer_code
101
+ end
102
+
103
+ message = response_body['message']
104
+
105
+ error_info = { message: message, debug_id: debug_id }
106
+
107
+ case code
108
+ when 1
109
+ raise GenericError.new(**error_info)
110
+ when 50
111
+ raise IncorrectParametersError.new(**error_info)
112
+ when 'C000'
113
+ raise GenericConsentError.new(**error_info)
114
+ when 'C001'
115
+ raise InvalidConsentError.new(**error_info)
116
+ when 'C002'
117
+ raise ConsentWithUnfinalizedProcessError.new(**error_info)
118
+ when 'C003'
119
+ raise ProductMismatchConsentError.new(**error_info)
120
+ when 'C004'
121
+ raise ExpiredConsentError.new(**error_info)
122
+ when 'C005'
123
+ raise MaximumNumberOfCallsReachedConsentError.new(**error_info)
124
+ when 'T000'
125
+ raise GenericTransactionError.new(**error_info)
126
+ when 'T001'
127
+ raise InvalidConsentForProductError.new(**error_info)
128
+ end
129
+
130
+ nil
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,9 @@
1
+ module AfterbanksPSD2
2
+ class Collection < Array
3
+ def initialize(response, item_klass)
4
+ response.each do |item|
5
+ self << item_klass.new(item)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ module AfterbanksPSD2
2
+ class Configuration
3
+ attr_accessor :servicekey, :logger
4
+ end
5
+ end
@@ -0,0 +1,76 @@
1
+ module AfterbanksPSD2
2
+ class Error < ::StandardError
3
+ attr_reader :message, :debug_id
4
+
5
+ def initialize(message:, debug_id:)
6
+ super
7
+
8
+ @message = message
9
+ @debug_id = debug_id
10
+ end
11
+
12
+ def code
13
+ raise 'Not implemented'
14
+ end
15
+ end
16
+
17
+ class GenericError < Error
18
+ def code
19
+ 1
20
+ end
21
+ end
22
+
23
+ class IncorrectParametersError < Error
24
+ def code
25
+ 50
26
+ end
27
+ end
28
+
29
+ class GenericConsentError < Error
30
+ def code
31
+ 'C000'
32
+ end
33
+ end
34
+
35
+ class InvalidConsentError < Error
36
+ def code
37
+ 'C001'
38
+ end
39
+ end
40
+
41
+ class ConsentWithUnfinalizedProcessError < Error
42
+ def code
43
+ 'C002'
44
+ end
45
+ end
46
+
47
+ class ProductMismatchConsentError < Error
48
+ def code
49
+ 'C003'
50
+ end
51
+ end
52
+
53
+ class ExpiredConsentError < Error
54
+ def code
55
+ 'C004'
56
+ end
57
+ end
58
+
59
+ class MaximumNumberOfCallsReachedConsentError < Error
60
+ def code
61
+ 'C005'
62
+ end
63
+ end
64
+
65
+ class GenericTransactionError < Error
66
+ def code
67
+ 'T000'
68
+ end
69
+ end
70
+
71
+ class InvalidConsentForProductError < Error
72
+ def code
73
+ 'T001'
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,89 @@
1
+ module AfterbanksPSD2
2
+ class Resource
3
+ def initialize(data)
4
+ data = sanitize_data(data)
5
+ generate_attr_readers
6
+ set_data(data)
7
+ end
8
+
9
+ def fields_information
10
+ self.class.fields_information
11
+ end
12
+
13
+ private
14
+
15
+ def sanitize_data(data)
16
+ data.reduce({}) do |hash, (key, value)|
17
+ hash[key.to_sym] = value
18
+
19
+ hash
20
+ end
21
+ end
22
+
23
+ def generate_attr_readers
24
+ fields_information.each do |field, _|
25
+ define_singleton_method(field) do
26
+ instance_variable_get("@#{field}")
27
+ end
28
+ end
29
+ end
30
+
31
+ def set_data(data)
32
+ fields_information.each do |official_name, field_information|
33
+ raw_value = nil
34
+
35
+ if (original_name = field_information[:original_name])
36
+ raw_value = data[original_name]
37
+ end
38
+
39
+ raw_value ||= data[official_name]
40
+
41
+ next if raw_value.nil?
42
+
43
+ type = field_information[:type]
44
+ value = value_for(raw_value, type)
45
+
46
+ instance_variable_set("@#{official_name}", value)
47
+ end
48
+ end
49
+
50
+ def value_for(raw_value, type)
51
+ case type
52
+ when :boolean
53
+ [true, "1", 1].include?(raw_value)
54
+ when :date
55
+ if raw_value.is_a?(Date)
56
+ raw_value
57
+ else
58
+ Date.parse(raw_value)
59
+ end
60
+ else
61
+ raw_value
62
+ end
63
+ end
64
+
65
+ def marshal_dump
66
+ dump = {}
67
+
68
+ fields_information.each do |field, _|
69
+ dump[field] = send(field)
70
+ end
71
+
72
+ dump
73
+ end
74
+
75
+ def marshal_load(serialized_resource)
76
+ initialize(serialized_resource)
77
+ end
78
+
79
+ class << self
80
+ def fields_information
81
+ @fields_information
82
+ end
83
+
84
+ def has_fields(fields_information)
85
+ @fields_information = fields_information
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,37 @@
1
+ module AfterbanksPSD2
2
+ class Account < Resource
3
+ has_fields product: { type: :string },
4
+ type: { type: :string },
5
+ balance: { type: :decimal },
6
+ countable_balance: { type: :decimal },
7
+ arranged_balance: { type: :decimal },
8
+ currency: { type: :string },
9
+ description: { type: :string },
10
+ iban: { type: :string },
11
+ is_owner: { type: :boolean },
12
+ holders: { type: :hash }
13
+
14
+ def self.list(token:)
15
+ params = {
16
+ servicekey: AfterbanksPSD2.configuration.servicekey,
17
+ token: token,
18
+ products: 'GLOBAL'
19
+ }
20
+
21
+ response, debug_id = AfterbanksPSD2.api_call(
22
+ method: :post,
23
+ path: '/transactions/',
24
+ params: params
25
+ )
26
+
27
+ Response.new(
28
+ result: Collection.new(
29
+ response,
30
+ self
31
+ ),
32
+ body: response,
33
+ debug_id: debug_id
34
+ )
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,73 @@
1
+ module AfterbanksPSD2
2
+ class Bank < Resource
3
+ has_fields country_code: { type: :string, original_name: :countryCode },
4
+ service: { type: :string },
5
+ swift: { type: :string },
6
+ fullname: { type: :string },
7
+ image: { type: :string },
8
+ image_svg: { type: :string, original_name: :imageSVG },
9
+ payments_supported: { type: :boolean, original_name: :paymentsSupported }
10
+
11
+ def self.list(ordered: false, country_codes: nil)
12
+ response, debug_id = AfterbanksPSD2.api_call(
13
+ method: :get,
14
+ path: '/listOfSupportedBanks/'
15
+ )
16
+
17
+ Response.new(
18
+ result: Collection.new(
19
+ banks_information_for(
20
+ response: response,
21
+ ordered: ordered,
22
+ country_codes: country_codes
23
+ ),
24
+ self
25
+ ),
26
+ body: response,
27
+ debug_id: debug_id
28
+ )
29
+ end
30
+
31
+ def self.banks_information_for(response:, ordered:, country_codes:)
32
+ country_codes = country_codes.map(&:upcase) unless country_codes.nil?
33
+ banks_information = []
34
+
35
+ response.each do |bank_information|
36
+ next if country_codes && !country_codes.include?(bank_information['countryCode'])
37
+
38
+ bank_information['fullname'] = bank_name_for(bank_information: bank_information)
39
+
40
+ banks_information << bank_information
41
+ end
42
+
43
+ if ordered
44
+ banks_information.sort! do |bank_information1, bank_information2|
45
+ bank_information1['fullname'].downcase <=> bank_information2['fullname'].downcase
46
+ end
47
+ end
48
+
49
+ banks_information
50
+ end
51
+
52
+ def self.bank_name_for(bank_information:)
53
+ service = bank_information['service']
54
+
55
+ # Name changes:
56
+
57
+ # 1. Rename Caja Ingenieros into Caixa d'Enginyers (most known name)
58
+ return "Caixa d'Enginyers" if service == 'cajaingenieros'
59
+
60
+ # 2. Rename Caixa Guisona into Caixa Guissona (fix typo)
61
+ return "Caixa Guissona" if service == 'caixaguissona'
62
+
63
+ # 3. Rename Caixa burriana into Caixa Burriana (fix typo)
64
+ return "Caixa Burriana" if service == 'caixaruralburriana'
65
+
66
+ bank_information['fullname']
67
+ end
68
+
69
+ def self.bank_id_for(bank_information:)
70
+ bank_information['service'].split("_").first
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,71 @@
1
+ module AfterbanksPSD2
2
+ class Transaction < Resource
3
+ has_fields date: { type: :date },
4
+ date2: { type: :date },
5
+ amount: { type: :decimal },
6
+ description: { type: :string },
7
+ balance: { type: :decimal },
8
+ transaction_id: { type: :string, original_name: :transactionId },
9
+ category_id: { type: :integer, original_name: :categoryId },
10
+ account: { type: :afterbankspsd2_account }
11
+
12
+ def self.list(token:, products:, start_date:)
13
+ params = {
14
+ servicekey: AfterbanksPSD2.configuration.servicekey,
15
+ token: token,
16
+ products: products,
17
+ startDate: start_date.strftime("%d-%m-%Y")
18
+ }
19
+
20
+ # if the start_date is older than 90 days, we need to increase timeout to 2 hours
21
+ if start_date < Date.today << 3
22
+ options = {
23
+ timeout: 7200
24
+ }
25
+ end
26
+
27
+ response, debug_id = AfterbanksPSD2.api_call(
28
+ method: :post,
29
+ path: '/transactions/',
30
+ params: params,
31
+ options: options
32
+ )
33
+
34
+ Response.new(
35
+ result: Collection.new(
36
+ transactions_information_for(
37
+ response: response,
38
+ products: products
39
+ ),
40
+ self
41
+ ),
42
+ body: response,
43
+ debug_id: debug_id
44
+ )
45
+ end
46
+
47
+ def self.transactions_information_for(response:, products:)
48
+ transactions_information = []
49
+ products_array = products.split(",")
50
+
51
+ response.each do |account_information|
52
+ product = account_information['product']
53
+
54
+ next unless products_array.include?(product)
55
+
56
+ transactions = account_information['transactions']
57
+ next if transactions.nil? || transactions.empty?
58
+
59
+ account = AfterbanksPSD2::Account.new(account_information)
60
+
61
+ transactions.each do |transaction|
62
+ transaction['account'] = account
63
+ end
64
+
65
+ transactions_information += transactions
66
+ end
67
+
68
+ transactions_information
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,25 @@
1
+ module AfterbanksPSD2
2
+ class User < Resource
3
+ has_fields limit: { type: :integer },
4
+ counter: { type: :integer },
5
+ remaining_calls: { type: :integer },
6
+ date_renewal: { type: :date },
7
+ detail: { type: :string }
8
+
9
+ def self.get
10
+ response, debug_id = AfterbanksPSD2.api_call(
11
+ method: :post,
12
+ path: '/me/',
13
+ params: {
14
+ servicekey: AfterbanksPSD2.configuration.servicekey
15
+ }
16
+ )
17
+
18
+ Response.new(
19
+ result: new(response),
20
+ body: response,
21
+ debug_id: debug_id
22
+ )
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,11 @@
1
+ module AfterbanksPSD2
2
+ class Response
3
+ attr_accessor :result, :body, :debug_id
4
+
5
+ def initialize(result:, body:, debug_id:)
6
+ @result = result
7
+ @body = body
8
+ @debug_id = debug_id
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module AfterbanksPSD2
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,12 @@
1
+ require 'afterbanks_psd2/version'
2
+ require 'afterbanks_psd2/configuration'
3
+ require 'afterbanks_psd2/base'
4
+ require 'afterbanks_psd2/error'
5
+ require 'afterbanks_psd2/resource'
6
+ require 'afterbanks_psd2/response'
7
+ require 'afterbanks_psd2/collection'
8
+
9
+ require 'afterbanks_psd2/resources/bank'
10
+ require 'afterbanks_psd2/resources/user'
11
+ require 'afterbanks_psd2/resources/account'
12
+ require 'afterbanks_psd2/resources/transaction'
metadata ADDED
@@ -0,0 +1,176 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: afterbanks-psd2-api-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Florent Lemaitre
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-10-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rest-client
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 2.0.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 2.0.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop-rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: timecop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: webmock
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description:
126
+ email:
127
+ - flemaitre@sellsy.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - ".circleci/config.yml"
133
+ - ".gitignore"
134
+ - ".rspec"
135
+ - ".rubocop.yml"
136
+ - ".rubocop_exclusions.yml"
137
+ - Gemfile
138
+ - Gemfile.lock
139
+ - README.md
140
+ - Rakefile
141
+ - afterbanks_psd2.gemspec
142
+ - lib/afterbanks_psd2.rb
143
+ - lib/afterbanks_psd2/base.rb
144
+ - lib/afterbanks_psd2/collection.rb
145
+ - lib/afterbanks_psd2/configuration.rb
146
+ - lib/afterbanks_psd2/error.rb
147
+ - lib/afterbanks_psd2/resource.rb
148
+ - lib/afterbanks_psd2/resources/account.rb
149
+ - lib/afterbanks_psd2/resources/bank.rb
150
+ - lib/afterbanks_psd2/resources/transaction.rb
151
+ - lib/afterbanks_psd2/resources/user.rb
152
+ - lib/afterbanks_psd2/response.rb
153
+ - lib/afterbanks_psd2/version.rb
154
+ homepage: https://getquipu.com
155
+ licenses: []
156
+ metadata: {}
157
+ post_install_message:
158
+ rdoc_options: []
159
+ require_paths:
160
+ - lib
161
+ required_ruby_version: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ required_rubygems_version: !ruby/object:Gem::Requirement
167
+ requirements:
168
+ - - ">="
169
+ - !ruby/object:Gem::Version
170
+ version: '0'
171
+ requirements: []
172
+ rubygems_version: 3.2.33
173
+ signing_key:
174
+ specification_version: 4
175
+ summary: Ruby client for the Afterbanks' PSD2 API
176
+ test_files: []