afterbanks-psd2-api-ruby 0.1.0
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/.circleci/config.yml +44 -0
- data/.gitignore +2 -0
- data/.rspec +2 -0
- data/.rubocop.yml +75 -0
- data/.rubocop_exclusions.yml +10 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +90 -0
- data/README.md +64 -0
- data/Rakefile +8 -0
- data/afterbanks_psd2.gemspec +32 -0
- data/lib/afterbanks_psd2/base.rb +133 -0
- data/lib/afterbanks_psd2/collection.rb +9 -0
- data/lib/afterbanks_psd2/configuration.rb +5 -0
- data/lib/afterbanks_psd2/error.rb +76 -0
- data/lib/afterbanks_psd2/resource.rb +89 -0
- data/lib/afterbanks_psd2/resources/account.rb +37 -0
- data/lib/afterbanks_psd2/resources/bank.rb +73 -0
- data/lib/afterbanks_psd2/resources/transaction.rb +71 -0
- data/lib/afterbanks_psd2/resources/user.rb +25 -0
- data/lib/afterbanks_psd2/response.rb +11 -0
- data/lib/afterbanks_psd2/version.rb +3 -0
- data/lib/afterbanks_psd2.rb +12 -0
- metadata +176 -0
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
data/.rspec
ADDED
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
|
data/Gemfile
ADDED
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
|
+
[](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,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,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,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: []
|