ibanity 1.6.0 → 1.10.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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +27 -0
  3. data/.github/workflows/gem-push.yml +29 -0
  4. data/CHANGELOG.md +35 -1
  5. data/README.md +1 -1
  6. data/ibanity.gemspec +1 -0
  7. data/lib/ibanity/api/base_resource.rb +50 -22
  8. data/lib/ibanity/api/flat_resource.rb +20 -0
  9. data/lib/ibanity/api/isabel_connect/access_token.rb +1 -0
  10. data/lib/ibanity/api/isabel_connect/bulk_payment_initiation_request.rb +4 -2
  11. data/lib/ibanity/api/isabel_connect/refresh_token.rb +3 -1
  12. data/lib/ibanity/api/isabel_connect/token.rb +39 -0
  13. data/lib/ibanity/api/ponto_connect/bulk_payment.rb +20 -0
  14. data/lib/ibanity/api/ponto_connect/onboarding_details.rb +10 -0
  15. data/lib/ibanity/api/ponto_connect/payment_activation_request.rb +10 -0
  16. data/lib/ibanity/api/ponto_connect/reauthorization_request.rb +10 -0
  17. data/lib/ibanity/api/ponto_connect/sandbox/financial_institution_transaction.rb +8 -0
  18. data/lib/ibanity/api/sandbox/financial_institution_transaction.rb +10 -0
  19. data/lib/ibanity/api/webhooks/key.rb +11 -0
  20. data/lib/ibanity/api/webhooks/ponto_connect.rb +24 -0
  21. data/lib/ibanity/api/webhooks/xs2a.rb +24 -0
  22. data/lib/ibanity/api/xs2a/transaction.rb +10 -5
  23. data/lib/ibanity/client.rb +78 -8
  24. data/lib/ibanity/error.rb +2 -0
  25. data/lib/ibanity/http_signature.rb +28 -5
  26. data/lib/ibanity/version.rb +1 -1
  27. data/lib/ibanity/webhook.rb +94 -0
  28. data/lib/ibanity.rb +24 -3
  29. data/spec/lib/ibanity/base_resource_spec.rb +42 -0
  30. data/spec/spec_helper.rb +6 -0
  31. data/spec/support/fixture.rb +8 -0
  32. data/spec/support/fixtures/json/relationships/data_with_type.json +21 -0
  33. data/spec/support/fixtures/json/relationships/data_without_type.json +20 -0
  34. data/spec/support/fixtures/json/relationships/meta_with_type.json +20 -0
  35. data/spec/support/fixtures/json/relationships/no_links_related.json +15 -0
  36. metadata +41 -4
  37. data/.travis.yml +0 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 210db871fa1e9b4953293fd68cb50e781e83bfd6b24b6a3ef45329f45b3e1951
4
- data.tar.gz: 90a0e99571a842e2634654f23ffe477a5b0f28e7a0af8ef93524afd27f00f40d
3
+ metadata.gz: 55b04259d7e3e1f8cd8ee53b23d5b50fd4803d0a15822870b59b2e3bd78820a2
4
+ data.tar.gz: 79003b1d051b9e517ea786dab240b19dca82bfcbf8379784eacccdd15a7e8481
5
5
  SHA512:
6
- metadata.gz: e7e221288a48eeddfb8b3facaa4e8f4320244cb8b06f579af646fca4b5c44c439a45a5170a8f7914924c4453b0f52b52b3fef2ac44fde1ba7cb64a1fbaa10214
7
- data.tar.gz: 73d19152ca69929356a0baf99b6bf2ce780aa392a2ad489997bf0ec3ba11a90b021bdf4347daff51f2f61e259ea53e8bbaa1f0d4bf8856ed016e259a91439d5d
6
+ metadata.gz: f11e1268e728697e6f75cdc0eb7a79358d8359ee4eada54ddf7e452ef7147b9f0c548724047a5d99454ca785840ab5e72c0b16448b587789d27f3b6ced89a47e
7
+ data.tar.gz: 96132c499b4e33ed9681f6998d1f7923e260d715078777a503dfd13c98a4979fcf03854fa96f217d3527a0e248cec994710fdfe74b3816afc92302d91ab60316
@@ -0,0 +1,27 @@
1
+ name: Ruby CI
2
+
3
+ on: [push]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+
9
+ steps:
10
+ - uses: actions/checkout@v2
11
+ - name: Set up Ruby
12
+ uses: ruby/setup-ruby@v1
13
+ with:
14
+ ruby-version: 2.6
15
+ - name: Install dependencies
16
+ run: bundle install
17
+ - name: Run tests
18
+ run: bundle exec rspec
19
+ - name: Notify slack failure on master branch
20
+ if: contains(github.ref, 'master') && failure()
21
+ env:
22
+ SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
23
+ uses: voxmedia/github-action-slack-notify-build@v1.1.2
24
+ with:
25
+ channel: ibanity-support-tech
26
+ status: FAILED
27
+ color: danger
@@ -0,0 +1,29 @@
1
+ name: Ruby Gem
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ build:
9
+ name: Build + Publish
10
+ runs-on: ubuntu-latest
11
+
12
+ steps:
13
+ - uses: actions/checkout@v2
14
+ - name: Set up Ruby 2.6
15
+ uses: actions/setup-ruby@v1
16
+ with:
17
+ ruby-version: 2.6
18
+
19
+ - name: Publish to RubyGems
20
+ run: |
21
+ mkdir -p $HOME/.gem
22
+ touch $HOME/.gem/credentials
23
+ chmod 0600 $HOME/.gem/credentials
24
+ printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
25
+ gem build *.gemspec
26
+ gem push *.gem
27
+ rm -rf $HOME/.gem/credentials
28
+ env:
29
+ GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
data/CHANGELOG.md CHANGED
@@ -1,5 +1,39 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.10
4
+
5
+ * [Ponto Connect] Add support for payment activation requests
6
+
7
+ * [Webhooks] Add support for webhook signature validation, keys endpoint, and events.
8
+
9
+ ## 1.9
10
+
11
+ * [Ponto Connect] Add account reauthorization requests
12
+
13
+ * [Isabel Connect] Deprecate `Ibanity::IsabelConnect::AccessToken` and `Ibanity::IsabelConnect::RefreshToken`, please use `Ibanity::IsabelConnect::Token` instead
14
+
15
+ ## 1.8
16
+
17
+ * [XS2A] Update sandbox transactions
18
+
19
+ * [XS2A] Add support to list updated transaction using `synchronization_id`
20
+
21
+ * [Ponto Connect] Add support to BulkPayments
22
+
23
+ * [Isabel Connect] Add `hideDetails` and `isShared` parameters to `BulkPaymentInititationRequest.create`
24
+
25
+ ## 1.7
26
+ ### Enhancements
27
+
28
+ * [Ponto Connect] Add support for the /onboarding-details endpoint.
29
+
30
+ ## 1.6
31
+ ### Enhancements
32
+
33
+ * [Ponto Connect] Added a new revoke account endpoint. It allows to remove an account from your integration. (The bank account will not be deleted from the Ponto account itself).
34
+
35
+ * [Ponto Connect] Added a new delete organization integration endpoint. It provides an alternative method to revoke the integration (in addition to the revoke refresh token endpoint). This endpoint remains accessible with a client access token, even if your refresh token is lost or expired.
36
+
3
37
  ## 1.5
4
38
 
5
39
  * Proper release of previous enhancements
@@ -18,7 +52,7 @@
18
52
 
19
53
  ### Enhancements
20
54
 
21
- * Default signature algorithm is now ["hs2019"](https://tools.ietf.org/html/draft-cavage-http-signatures-12#appendix-E.2)
55
+ * Default signature algorithm is now ["hs2019"](https://tools.ietf.org/html/draft-cavage-http-signatures-12#appendix-E.2)
22
56
  * Add support for periodic and bulk payments
23
57
  * Add snake-case transformation for deeply nested data structures
24
58
 
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Build Status](https://travis-ci.org/ibanity/ibanity-ruby.svg?branch=master)](https://travis-ci.org/ibanity/ibanity-ruby)
1
+ ![Ruby CI](https://github.com/ibanity/ibanity-ruby/workflows/Ruby%20CI/badge.svg?branch=master)
2
2
 
3
3
  # Ibanity Ruby Library
4
4
 
data/ibanity.gemspec CHANGED
@@ -19,5 +19,6 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_dependency "rest-client", ">= 1.8.0"
22
+ spec.add_dependency "jose", ">= 1.1.3"
22
23
  spec.add_development_dependency "rspec", "3.9.0"
23
24
  end
@@ -86,6 +86,8 @@ module Ibanity
86
86
  private
87
87
 
88
88
  def prepare_attributes(raw)
89
+ raise "Unexpected raw type, expected hash, got #{raw}" unless raw.is_a?(Hash)
90
+
89
91
  base = {
90
92
  "id" => raw["id"],
91
93
  }
@@ -97,23 +99,47 @@ module Ibanity
97
99
 
98
100
  def setup_relationships(relationships, customer_access_token = nil)
99
101
  relationships.each do |key, relationship|
100
- if relationship["data"]
101
- self[Ibanity::Util.underscore("#{key}_id")] = relationship["data"]["id"]
102
- return unless relationship.dig("links", "related")
103
-
104
- klass = relationship_klass(key)
105
- method_name = Ibanity::Util.underscore(key)
106
- define_singleton_method(method_name) do |headers: nil|
107
- klass.find_by_uri(uri: relationship["links"]["related"], headers: headers, customer_access_token: customer_access_token)
108
- end
109
- else
110
- singular_key = key[0..-2]
111
- klass = relationship_klass(singular_key)
112
- method_name = Ibanity::Util.underscore(key)
113
- define_singleton_method(method_name) do |headers: nil, **query_params|
114
- uri = relationship["links"]["related"]
115
- klass.list_by_uri(uri: uri, headers: headers, query_params: query_params, customer_access_token: customer_access_token)
116
- end
102
+ url = relationship.dig("links", "related")
103
+ id = relationship.dig("data", "id")
104
+
105
+ if url
106
+ setup_relationship(customer_access_token, key, relationship, url)
107
+ elsif id
108
+ self[Ibanity::Util.underscore("#{key}_id")] = id
109
+ end
110
+ end
111
+ end
112
+
113
+ def setup_relationship(customer_access_token, key, relationship, url)
114
+ if relationship["data"]
115
+ resource = relationship.dig("data", "type") || key
116
+ klass = relationship_klass(resource)
117
+ method_name = Ibanity::Util.underscore(key)
118
+ define_singleton_method(method_name) do |headers: nil|
119
+ klass.find_by_uri(uri: url, headers: headers, customer_access_token: customer_access_token)
120
+ end
121
+ self[Ibanity::Util.underscore("#{key}_id")] = relationship.dig("data", "id")
122
+ elsif relationship["meta"]
123
+ resource = relationship.dig("meta", "type")
124
+ klass = relationship_klass(resource)
125
+ method_name = Ibanity::Util.underscore(key)
126
+ define_singleton_method(method_name) do |headers: nil|
127
+ klass.find_by_uri(uri: url, headers: headers, customer_access_token: customer_access_token)
128
+ end
129
+ elsif relationship.dig("links", "meta", "type")
130
+ resource = relationship.dig("links", "meta", "type")
131
+ klass = relationship_klass(resource)
132
+ method_name = Ibanity::Util.underscore(key)
133
+ define_singleton_method(method_name) do |headers: nil|
134
+ klass.list_by_uri(uri: url, headers: headers, customer_access_token: customer_access_token)
135
+ end
136
+ else
137
+ resource = key
138
+ singular_resource = resource[0..-2]
139
+ klass = relationship_klass(singular_resource)
140
+ method_name = Ibanity::Util.underscore(resource)
141
+ define_singleton_method(method_name) do |headers: nil, **query_params|
142
+ klass.list_by_uri(uri: url, headers: headers, query_params: query_params, customer_access_token: customer_access_token)
117
143
  end
118
144
  end
119
145
  end
@@ -126,11 +152,13 @@ module Ibanity
126
152
 
127
153
  def relationship_klass(name)
128
154
  camelized_name = Ibanity::Util.camelize(name)
129
- enclosing_module = if camelized_name == "FinancialInstitution"
130
- Ibanity::Xs2a
131
- else
132
- Object.const_get(self.class.to_s.split("::")[0...-1].join("::"))
133
- end
155
+ enclosing_module =
156
+ if camelized_name == "FinancialInstitution"
157
+ Ibanity::Xs2a
158
+ else
159
+ Object.const_get(self.class.to_s.split("::")[0...-1].join("::"))
160
+ end
161
+
134
162
  enclosing_module.const_get(camelized_name)
135
163
  end
136
164
  end
@@ -0,0 +1,20 @@
1
+ module Ibanity
2
+ class FlatResource < OpenStruct
3
+ def self.list_by_uri(uri:, key:)
4
+ raw_response = Ibanity.client.get(uri: uri)
5
+ items = raw_response[key].map { |raw_item| new(raw_item) }
6
+ Ibanity::Collection.new(
7
+ klass: self,
8
+ items: items,
9
+ links: nil,
10
+ paging: nil,
11
+ synchronized_at: nil,
12
+ latest_synchronization: nil
13
+ )
14
+ end
15
+
16
+ def initialize(raw)
17
+ super(raw)
18
+ end
19
+ end
20
+ end
@@ -2,6 +2,7 @@ module Ibanity
2
2
  module IsabelConnect
3
3
  class AccessToken < Ibanity::OAuthResource
4
4
  def self.create(refresh_token:, idempotency_key: nil)
5
+ warn "WARNING: Ibanity::IsabelConnect::AccessToken.create is deprecated, please use Ibanity::IsabelConnect::Token.create instead"
5
6
  uri = Ibanity.isabel_connect_api_schema["oAuth2"]["accessTokens"]
6
7
  arguments = [
7
8
  ["grant_type", "refresh_token"],
@@ -1,7 +1,7 @@
1
1
  module Ibanity
2
2
  module IsabelConnect
3
3
  class BulkPaymentInitiationRequest < Ibanity::BaseResource
4
- def self.create(access_token:, raw_content:, filename:, idempotency_key: nil)
4
+ def self.create(access_token:, raw_content:, filename:, idempotency_key: nil, is_shared: true, hide_details: false)
5
5
  uri = Ibanity.isabel_connect_api_schema["bulkPaymentInitiationRequests"].sub("{bulkPaymentInitiationRequestId}", "")
6
6
  create_file_by_uri(
7
7
  uri: uri,
@@ -11,7 +11,9 @@ module Ibanity
11
11
  idempotency_key: idempotency_key,
12
12
  headers: {
13
13
  content_type: :xml,
14
- "Content-Disposition": "inline; filename=#{filename}"
14
+ "Content-Disposition": "inline; filename=#{filename}",
15
+ "Is-Shared": is_shared,
16
+ "Hide-Details": hide_details
15
17
  }
16
18
  )
17
19
  end
@@ -2,6 +2,7 @@ module Ibanity
2
2
  module IsabelConnect
3
3
  class RefreshToken < Ibanity::OAuthResource
4
4
  def self.create(authorization_code:, redirect_uri:, idempotency_key: nil)
5
+ warn "WARNING: Ibanity::IsabelConnect::RefreshToken.create is deprecated, please use Ibanity::IsabelConnect::Token.create instead"
5
6
  uri = Ibanity.isabel_connect_api_schema["oAuth2"]["refreshTokens"]["create"]
6
7
  arguments = [
7
8
  ["grant_type", "authorization_code"],
@@ -13,8 +14,9 @@ module Ibanity
13
14
  payload = URI.encode_www_form(arguments)
14
15
  create_by_uri(uri: uri, payload: payload, idempotency_key: idempotency_key)
15
16
  end
16
-
17
+
17
18
  def self.delete(token:)
19
+ warn "WARNING: Ibanity::IsabelConnect::RefreshToken.delete is deprecated, please use Ibanity::IsabelConnect::Token.delete instead"
18
20
  uri = Ibanity.isabel_connect_api_schema["oAuth2"]["refreshTokens"]["revoke"]
19
21
  arguments = [
20
22
  ["token", token],
@@ -0,0 +1,39 @@
1
+ module Ibanity
2
+ module IsabelConnect
3
+ class Token < Ibanity::OAuthResource
4
+ def self.create(refresh_token: nil, authorization_code: nil, redirect_uri: nil, idempotency_key: nil)
5
+ uri = Ibanity.isabel_connect_api_schema["oAuth2"]["token"]
6
+ arguments =
7
+ if refresh_token
8
+ [
9
+ ["grant_type", "refresh_token"],
10
+ ["refresh_token", refresh_token],
11
+ ["client_id", Ibanity.client.isabel_connect_client_id],
12
+ ["client_secret", Ibanity.client.isabel_connect_client_secret]
13
+ ]
14
+ elsif authorization_code
15
+ [
16
+ ["grant_type", "authorization_code"],
17
+ ["code", authorization_code],
18
+ ["client_id", Ibanity.client.isabel_connect_client_id],
19
+ ["client_secret", Ibanity.client.isabel_connect_client_secret],
20
+ ["redirect_uri", redirect_uri]
21
+ ]
22
+ end
23
+ payload = URI.encode_www_form(arguments)
24
+ create_by_uri(uri: uri, payload: payload, idempotency_key: idempotency_key)
25
+ end
26
+
27
+ def self.delete(token:)
28
+ uri = Ibanity.isabel_connect_api_schema["oAuth2"]["revoke"]
29
+ arguments = [
30
+ ["token", token],
31
+ ["client_id", Ibanity.client.isabel_connect_client_id],
32
+ ["client_secret", Ibanity.client.isabel_connect_client_secret]
33
+ ]
34
+ payload = URI.encode_www_form(arguments)
35
+ create_by_uri(uri: uri, payload: payload)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,20 @@
1
+ module Ibanity
2
+ module PontoConnect
3
+ class BulkPayment < Ibanity::BaseResource
4
+ def self.find(access_token:, account_id:, id:)
5
+ uri = Ibanity.ponto_connect_api_schema["account"]["bulkPayments"].sub("{accountId}", account_id).sub("{bulkPaymentId}", id)
6
+ find_by_uri(uri: uri, customer_access_token: access_token)
7
+ end
8
+
9
+ def self.create(account_id:, access_token: nil, **attributes)
10
+ uri = Ibanity.ponto_connect_api_schema["account"]["bulkPayments"].gsub("{accountId}", account_id).gsub("{bulkPaymentId}", "")
11
+ create_by_uri(uri: uri, resource_type: "bulkPayment", attributes: attributes, customer_access_token: access_token)
12
+ end
13
+
14
+ def self.delete(id:, account_id:, access_token:)
15
+ uri = Ibanity.ponto_connect_api_schema["account"]["bulkPayments"].gsub("{accountId}", account_id).gsub("{bulkPaymentId}", id)
16
+ destroy_by_uri(uri: uri, customer_access_token: access_token)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,10 @@
1
+ module Ibanity
2
+ module PontoConnect
3
+ class OnboardingDetails < Ibanity::BaseResource
4
+ def self.create(client_access_token:, **attributes)
5
+ uri = Ibanity.ponto_connect_api_schema["onboardingDetails"]
6
+ create_by_uri(uri: uri, resource_type: "onboardingDetails", attributes: attributes, customer_access_token: client_access_token)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module Ibanity
2
+ module PontoConnect
3
+ class PaymentActivationRequest < Ibanity::BaseResource
4
+ def self.create(access_token:, **attributes)
5
+ uri = Ibanity.ponto_connect_api_schema["paymentActivationRequests"]
6
+ create_by_uri(uri: uri, resource_type: "paymentActivationRequest", attributes: attributes, customer_access_token: access_token)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module Ibanity
2
+ module PontoConnect
3
+ class ReauthorizationRequest < Ibanity::BaseResource
4
+ def self.create(account_id:, access_token:, **attributes)
5
+ uri = Ibanity.ponto_connect_api_schema["account"]["reauthorizationRequests"].gsub("{accountId}", account_id)
6
+ create_by_uri(uri: uri, resource_type: "reauthorizationRequest", attributes: attributes, customer_access_token: access_token)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -25,6 +25,14 @@ module Ibanity
25
25
  .sub("{financialInstitutionTransactionId}", "")
26
26
  create_by_uri(uri: uri, resource_type: "financialInstitutionTransaction", attributes: attributes, customer_access_token: access_token)
27
27
  end
28
+
29
+ def self.update(access_token:, id:, financial_institution_id:, financial_institution_account_id:, **attributes)
30
+ uri = Ibanity.ponto_connect_api_schema["sandbox"]["financialInstitution"]["financialInstitutionAccount"]["financialInstitutionTransactions"]
31
+ .sub("{financialInstitutionId}", financial_institution_id)
32
+ .sub("{financialInstitutionAccountId}", financial_institution_account_id)
33
+ .sub("{financialInstitutionTransactionId}", id)
34
+ update_by_uri(uri: uri, resource_type: "financialInstitutionTransaction", attributes: attributes, customer_access_token: access_token)
35
+ end
28
36
  end
29
37
  end
30
38
  end
@@ -31,6 +31,16 @@ module Ibanity
31
31
  find_by_uri(uri: uri)
32
32
  end
33
33
 
34
+ def self.update(id:, financial_institution_user_id:, financial_institution_id:, financial_institution_account_id:, idempotency_key: nil, **attributes)
35
+ path = Ibanity.sandbox_api_schema["financialInstitution"]["financialInstitutionAccount"]["financialInstitutionTransactions"]
36
+ .gsub("{financialInstitutionId}", financial_institution_id)
37
+ .gsub("{financialInstitutionUserId}", financial_institution_user_id)
38
+ .gsub("{financialInstitutionAccountId}", financial_institution_account_id)
39
+ .gsub("{financialInstitutionTransactionId}", id)
40
+ uri = Ibanity.client.build_uri(path)
41
+ update_by_uri(uri: uri, resource_type: "financialInstitutionTransaction", attributes: attributes, idempotency_key: idempotency_key)
42
+ end
43
+
34
44
  def self.delete(id:, financial_institution_user_id:, financial_institution_id:, financial_institution_account_id:)
35
45
  path = Ibanity.sandbox_api_schema["financialInstitution"]["financialInstitutionAccount"]["financialInstitutionTransactions"]
36
46
  .gsub("{financialInstitutionId}", financial_institution_id)
@@ -0,0 +1,11 @@
1
+ module Ibanity
2
+ module Webhooks
3
+ class Key < Ibanity::FlatResource
4
+ def self.list
5
+ path = Ibanity.webhooks_api_schema["keys"]
6
+ uri = Ibanity.client.build_uri(path)
7
+ list_by_uri(uri: uri, key: "keys")
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,24 @@
1
+ module Ibanity
2
+ module Webhooks
3
+ module PontoConnect
4
+ module Synchronization
5
+ class SucceededWithoutChange < Ibanity::BaseResource
6
+ end
7
+
8
+ class Failed < Ibanity::BaseResource
9
+ end
10
+ end
11
+
12
+ module Account
13
+ class DetailsUpdated < Ibanity::BaseResource
14
+ end
15
+
16
+ class TransactionsCreated < Ibanity::BaseResource
17
+ end
18
+
19
+ class TransactionsUpdated < Ibanity::BaseResource
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ module Ibanity
2
+ module Webhooks
3
+ module Xs2a
4
+ module Synchronization
5
+ class SucceededWithoutChange < Ibanity::BaseResource
6
+ end
7
+
8
+ class Failed < Ibanity::BaseResource
9
+ end
10
+ end
11
+
12
+ module Account
13
+ class DetailsUpdated < Ibanity::BaseResource
14
+ end
15
+
16
+ class TransactionsCreated < Ibanity::BaseResource
17
+ end
18
+
19
+ class TransactionsUpdated < Ibanity::BaseResource
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,11 +1,16 @@
1
1
  module Ibanity
2
2
  module Xs2a
3
3
  class Transaction < Ibanity::BaseResource
4
- def self.list(financial_institution_id:, account_id:, customer_access_token:, headers: nil, **query_params)
5
- uri = Ibanity.xs2a_api_schema["customer"]["financialInstitution"]["transactions"]
6
- .sub("{financialInstitutionId}", financial_institution_id)
7
- .sub("{accountId}", account_id)
8
- .sub("{transactionId}", "")
4
+ def self.list(financial_institution_id: nil, account_id: nil, synchronization_id: nil, customer_access_token:, headers: nil, **query_params)
5
+ uri = if synchronization_id
6
+ Ibanity.xs2a_api_schema["customer"]["synchronization"]["updatedTransactions"]
7
+ .sub("{synchronizationId}", synchronization_id)
8
+ else
9
+ Ibanity.xs2a_api_schema["customer"]["financialInstitution"]["transactions"]
10
+ .sub("{financialInstitutionId}", financial_institution_id)
11
+ .sub("{accountId}", account_id)
12
+ .sub("{transactionId}", "")
13
+ end
9
14
  list_by_uri(uri: uri, query_params: query_params, customer_access_token: customer_access_token, headers: headers)
10
15
  end
11
16
 
@@ -1,22 +1,40 @@
1
1
  module Ibanity
2
2
  class Client
3
3
 
4
- attr_reader :base_uri, :signature_certificate, :signature_key, :isabel_connect_client_id, :isabel_connect_client_secret, :ponto_connect_client_id, :ponto_connect_client_secret
4
+ attr_reader :base_uri, :signature_certificate, :signature_key, :isabel_connect_client_id, :isabel_connect_client_secret, :ponto_connect_client_id, :ponto_connect_client_secret, :application_id
5
5
 
6
- def initialize(certificate:, key:, key_passphrase:, signature_certificate: nil, signature_certificate_id: nil, signature_key: nil, signature_key_passphrase: nil, api_scheme: "https", api_host: "api.ibanity.com", api_port: "443", ssl_ca_file: nil, isabel_connect_client_id: "valid_client_id", isabel_connect_client_secret: "valid_client_secret", ponto_connect_client_id: nil, ponto_connect_client_secret: nil)
6
+ def initialize(
7
+ certificate:,
8
+ key:,
9
+ key_passphrase:,
10
+ signature_certificate: nil,
11
+ signature_certificate_id: nil,
12
+ signature_key: nil,
13
+ signature_key_passphrase: nil,
14
+ api_scheme: "https",
15
+ api_host: "api.ibanity.com",
16
+ ssl_ca_file: nil,
17
+ isabel_connect_client_id: "valid_client_id",
18
+ isabel_connect_client_secret: "valid_client_secret",
19
+ ponto_connect_client_id: nil,
20
+ ponto_connect_client_secret: nil,
21
+ application_id: nil,
22
+ debug_http_requests: false)
7
23
  @isabel_connect_client_id = isabel_connect_client_id
8
24
  @isabel_connect_client_secret = isabel_connect_client_secret
9
25
  @ponto_connect_client_id = ponto_connect_client_id
10
26
  @ponto_connect_client_secret = ponto_connect_client_secret
11
27
  @certificate = OpenSSL::X509::Certificate.new(certificate)
12
28
  @key = OpenSSL::PKey::RSA.new(key, key_passphrase)
29
+ @http_debug = debug_http_requests
13
30
  if signature_certificate
14
31
  @signature_certificate = OpenSSL::X509::Certificate.new(signature_certificate)
15
32
  @signature_certificate_id = signature_certificate_id
16
33
  @signature_key = OpenSSL::PKey::RSA.new(signature_key, signature_key_passphrase)
17
34
  end
18
- @base_uri = "#{api_scheme}://#{api_host}:#{api_port}"
35
+ @base_uri = "#{api_scheme}://#{api_host}"
19
36
  @ssl_ca_file = ssl_ca_file
37
+ @application_id = application_id
20
38
  end
21
39
 
22
40
  def get(uri:, query_params: {}, customer_access_token: nil, headers: nil, json: true)
@@ -25,12 +43,12 @@ module Ibanity
25
43
  end
26
44
 
27
45
  def post(uri:, payload:, query_params: {}, customer_access_token: nil, idempotency_key: nil, json: true, headers: nil)
28
- headers = build_headers(customer_access_token: customer_access_token, idempotency_key: idempotency_key, extra_headers: headers, json: json)
46
+ headers = build_headers(customer_access_token: customer_access_token, idempotency_key: idempotency_key, extra_headers: headers, json: json, payload: payload)
29
47
  execute(method: :post, uri: uri, headers: headers, query_params: query_params, payload: payload, json: json)
30
48
  end
31
49
 
32
50
  def patch(uri:, payload:, query_params: {}, customer_access_token: nil, idempotency_key: nil, json: true)
33
- headers = build_headers(customer_access_token: customer_access_token, idempotency_key: idempotency_key, json: json)
51
+ headers = build_headers(customer_access_token: customer_access_token, idempotency_key: idempotency_key, json: json, payload: payload)
34
52
  execute(method: :patch, uri: uri, headers: headers, query_params: query_params, payload: payload, json: json)
35
53
  end
36
54
 
@@ -46,6 +64,15 @@ module Ibanity
46
64
  private
47
65
 
48
66
  def execute(method:, uri:, headers:, query_params: {}, payload: nil, json:)
67
+ case payload
68
+ when NilClass
69
+ payload = ''
70
+ when Hash
71
+ payload = json ? payload.to_json : payload
72
+ when Pathname
73
+ payload = File.open(payload, 'rb')
74
+ end
75
+
49
76
  if @signature_certificate
50
77
  signature = Ibanity::HttpSignature.new(
51
78
  certificate: @signature_certificate,
@@ -55,23 +82,30 @@ module Ibanity
55
82
  uri: uri,
56
83
  query_params: query_params,
57
84
  headers: headers,
58
- payload: payload && json ? payload.to_json : payload
85
+ payload: payload
59
86
  )
60
87
  headers.merge!(signature.signature_headers)
61
88
  end
89
+
62
90
  query = {
63
91
  method: method,
64
92
  url: uri,
65
93
  headers: headers.merge(params: query_params),
66
- payload: payload && json ? payload.to_json : payload,
94
+ payload: payload,
67
95
  ssl_client_cert: @certificate,
68
96
  ssl_client_key: @key,
69
97
  ssl_ca_file: @ssl_ca_file
70
98
  }
99
+
100
+ log("HTTP Request", query) if @http_debug
101
+
71
102
  raw_response = RestClient::Request.execute(query) do |response, request, result, &block|
103
+ log("HTTP response", { status: response.code, headers: response.headers, body: response.body }) if @http_debug
104
+
72
105
  if response.code >= 400
73
106
  ibanity_request_id = response.headers[:ibanity_request_id]
74
107
  body = JSON.parse(response.body)
108
+
75
109
  raise Ibanity::Error.new(body["errors"] || body, ibanity_request_id), "Ibanity request failed."
76
110
  else
77
111
  response.return!(&block)
@@ -80,12 +114,15 @@ module Ibanity
80
114
  JSON.parse(raw_response)
81
115
  rescue JSON::ParserError => e
82
116
  return raw_response.body
117
+ ensure
118
+ payload.close if payload.is_a?(File)
83
119
  end
84
120
 
85
- def build_headers(customer_access_token: nil, idempotency_key: nil, extra_headers: nil, json:)
121
+ def build_headers(customer_access_token: nil, idempotency_key: nil, extra_headers: nil, json:, payload: nil)
86
122
  headers = {
87
123
  accept: :json,
88
124
  }
125
+ headers["Transfer-Encoding"] = "chunked" if payload.is_a?(Pathname)
89
126
  headers[:content_type] = :json if json
90
127
  headers["Authorization"] = "Bearer #{customer_access_token}" unless customer_access_token.nil?
91
128
  headers["Ibanity-Idempotency-Key"] = idempotency_key unless idempotency_key.nil?
@@ -95,5 +132,38 @@ module Ibanity
95
132
  headers.merge(extra_headers)
96
133
  end
97
134
  end
135
+
136
+ def log(tag, info)
137
+ unless info.is_a?(Hash)
138
+ puts "[DEBUG] #{tag}: #{info}"
139
+ return
140
+ end
141
+
142
+ info = JSON.parse(info.to_json)
143
+
144
+ if info.dig("headers", "Authorization")
145
+ info["headers"]["Authorization"] = "[filtered]"
146
+ end
147
+ info.delete("proxy")
148
+ info.delete("ssl_client_cert")
149
+ info.delete("ssl_client_key")
150
+ if info.dig("payload").is_a?(Hash) && info.dig("payload", "client_secret")
151
+ info["payload"]["client_secret"] = "[filtered]"
152
+ end
153
+
154
+ if info["body"]&.is_a?(String)
155
+ begin
156
+ info["body"] = JSON.parse(info["body"])
157
+ rescue => exception
158
+ info["body"] = Base64.strict_encode64(info["body"])
159
+ end
160
+ end
161
+
162
+ begin
163
+ puts "[DEBUG] #{tag}: #{info.to_json}"
164
+ rescue => e
165
+ puts "[DEBUG] #{tag}: #{info}"
166
+ end
167
+ end
98
168
  end
99
169
  end
data/lib/ibanity/error.rb CHANGED
@@ -22,6 +22,8 @@ module Ibanity
22
22
  formatted_errors.join("\n")
23
23
  elsif @errors.is_a?(Hash)
24
24
  "* #{@errors["error"]}: #{@errors["error_description"]}\n * Error hint: #{@errors["error_hint"]}\n * ibanity_request_id: #{@ibanity_request_id}"
25
+ elsif @errors.is_a?(String)
26
+ @errors
25
27
  else
26
28
  super
27
29
  end
@@ -1,4 +1,5 @@
1
1
  require "base64"
2
+ require "pathname"
2
3
 
3
4
  module Ibanity
4
5
  class HttpSignature
@@ -29,16 +30,38 @@ module Ibanity
29
30
  }
30
31
  end
31
32
 
32
- private
33
-
34
33
  def payload_digest
35
- digest = OpenSSL::Digest::SHA512.new
36
- string_payload = @payload.nil? ? "" : @payload
37
- digest.update(string_payload)
34
+ @payload_digest ||= compute_digest
35
+ end
36
+
37
+ def compute_digest
38
+ case @payload
39
+ when NilClass
40
+ digest = compute_digest_string("")
41
+ when String
42
+ digest = compute_digest_string(@payload)
43
+ when File
44
+ digest = compute_digest_file(@payload)
45
+ end
38
46
  base64 = Base64.urlsafe_encode64(digest.digest)
39
47
  "SHA-512=#{base64}"
40
48
  end
41
49
 
50
+ def compute_digest_string(str)
51
+ digest = OpenSSL::Digest::SHA512.new
52
+ digest.update(str)
53
+ end
54
+
55
+ def compute_digest_file(pathname)
56
+ digest = OpenSSL::Digest::SHA512.new
57
+ File.open(pathname, 'rb') do |f|
58
+ while buffer = f.read(256_000)
59
+ digest << buffer
60
+ end
61
+ end
62
+ digest
63
+ end
64
+
42
65
  def headers_to_sign
43
66
  result = ["(request-target)", "host", "digest", "(created)"]
44
67
  result << "authorization" unless @headers["Authorization"].nil?
@@ -1,3 +1,3 @@
1
1
  module Ibanity
2
- VERSION = "1.6.0"
2
+ VERSION = "1.10.0"
3
3
  end
@@ -0,0 +1,94 @@
1
+ module Ibanity
2
+ module Webhook
3
+ DEFAULT_TOLERANCE = 30
4
+ SIGNING_ALGORITHM = "RS512"
5
+
6
+ # Initializes an Ibanity Webhooks event object from a JSON payload.
7
+ #
8
+ # This may raise JSON::ParserError if the payload is not valid JSON, or
9
+ # Ibanity::Error if the signature verification fails.
10
+ def self.construct_event!(payload, signature_header, tolerance: DEFAULT_TOLERANCE)
11
+ Signature.verify!(payload, signature_header, tolerance)
12
+
13
+ raw_item = JSON.parse(payload)
14
+ klass = raw_item["data"]["type"].split(".").map{|klass| klass.sub(/\S/, &:upcase)}.join("::")
15
+ Ibanity::Webhooks.const_get(klass).new(raw_item["data"])
16
+ end
17
+
18
+ module Signature
19
+ # Verifies the signature header for a given payload.
20
+ #
21
+ # Raises an Ibanity::Error in the following cases:
22
+ # - the header does not match the expected format
23
+ # - the digest does not match the payload
24
+ # - the issued at or expiration timestamp is not within the tolerance
25
+ # - the audience or issuer does not match the application config
26
+ #
27
+ # Returns true otherwise
28
+ def self.verify!(payload, signature_header, tolerance)
29
+ begin
30
+ key_details = JOSE::JWT.peek_protected(signature_header).to_hash
31
+ raise unless key_details["alg"] == SIGNING_ALGORITHM && key_details["kid"]
32
+ rescue
33
+ raise Ibanity::Error.new("Key details could not be parsed from the header", nil)
34
+ end
35
+
36
+ key = Ibanity.webhook_keys.find { |key| key.kid == key_details["kid"] }
37
+
38
+ if key.nil?
39
+ raise Ibanity::Error.new("The key id from the header didn't match an available signing key", nil)
40
+ end
41
+ signer = JOSE::JWK.from(key.to_h {|key, value| [key.to_s, value] })
42
+ verified, claims_string, _jws = JOSE::JWT.verify_strict(signer, [SIGNING_ALGORITHM], signature_header)
43
+
44
+ raise Ibanity::Error.new("The signature verification failed", nil) unless verified
45
+
46
+ jwt = JOSE::JWT.from(claims_string)
47
+
48
+ validate_digest!(jwt, payload)
49
+ validate_issued_at!(jwt, tolerance)
50
+ validate_expiration!(jwt, tolerance)
51
+ validate_issuer!(jwt)
52
+ validate_audience!(jwt)
53
+
54
+ true
55
+ end
56
+
57
+ private
58
+
59
+ def self.validate_digest!(jwt, payload)
60
+ unless Digest::SHA512.base64digest(payload) == jwt.fields["digest"]
61
+ raise_invalid_signature_error!("digest")
62
+ end
63
+ end
64
+
65
+ def self.validate_issued_at!(jwt, tolerance)
66
+ unless jwt.fields["iat"] <= Time.now.to_i + tolerance
67
+ raise_invalid_signature_error!("iat")
68
+ end
69
+ end
70
+
71
+ def self.validate_expiration!(jwt, tolerance)
72
+ unless jwt.fields["exp"] >= Time.now.to_i - tolerance
73
+ raise_invalid_signature_error!("exp")
74
+ end
75
+ end
76
+
77
+ def self.validate_issuer!(jwt)
78
+ unless jwt.fields["iss"] == Ibanity.client.base_uri
79
+ raise_invalid_signature_error!("iss")
80
+ end
81
+ end
82
+
83
+ def self.validate_audience!(jwt)
84
+ unless jwt.fields["aud"] == Ibanity.client.application_id
85
+ raise_invalid_signature_error!("aud")
86
+ end
87
+ end
88
+
89
+ def self.raise_invalid_signature_error!(field)
90
+ raise Ibanity::Error.new("The signature #{field} is invalid", nil)
91
+ end
92
+ end
93
+ end
94
+ end
data/lib/ibanity.rb CHANGED
@@ -4,6 +4,7 @@ require "uri"
4
4
  require "rest_client"
5
5
  require "json"
6
6
  require "securerandom"
7
+ require "jose"
7
8
 
8
9
  require_relative "ibanity/util"
9
10
  require_relative "ibanity/error"
@@ -11,6 +12,8 @@ require_relative "ibanity/collection"
11
12
  require_relative "ibanity/client"
12
13
  require_relative "ibanity/http_signature"
13
14
  require_relative "ibanity/api/base_resource"
15
+ require_relative "ibanity/api/flat_resource"
16
+ require_relative "ibanity/webhook"
14
17
  require_relative "ibanity/api/xs2a/account"
15
18
  require_relative "ibanity/api/xs2a/transaction"
16
19
  require_relative "ibanity/api/xs2a/holding"
@@ -33,6 +36,7 @@ require_relative "ibanity/api/isabel_connect/intraday_transaction"
33
36
  require_relative "ibanity/api/isabel_connect/account_report"
34
37
  require_relative "ibanity/api/isabel_connect/access_token"
35
38
  require_relative "ibanity/api/isabel_connect/refresh_token"
39
+ require_relative "ibanity/api/isabel_connect/token"
36
40
  require_relative "ibanity/api/isabel_connect/bulk_payment_initiation_request"
37
41
  require_relative "ibanity/api/sandbox/financial_institution_account"
38
42
  require_relative "ibanity/api/sandbox/financial_institution_transaction"
@@ -46,17 +50,24 @@ require_relative "ibanity/api/ponto_connect/synchronization"
46
50
  require_relative "ibanity/api/consent/consent"
47
51
  require_relative "ibanity/api/consent/processing_operation"
48
52
  require_relative "ibanity/api/ponto_connect/payment"
53
+ require_relative "ibanity/api/ponto_connect/bulk_payment"
49
54
  require_relative "ibanity/api/ponto_connect/user_info"
50
55
  require_relative "ibanity/api/ponto_connect/usage"
51
56
  require_relative "ibanity/api/ponto_connect/integration"
52
57
  require_relative "ibanity/api/ponto_connect/sandbox/financial_institution_account"
53
58
  require_relative "ibanity/api/ponto_connect/sandbox/financial_institution_transaction"
59
+ require_relative "ibanity/api/ponto_connect/onboarding_details"
60
+ require_relative "ibanity/api/ponto_connect/reauthorization_request"
61
+ require_relative "ibanity/api/ponto_connect/payment_activation_request"
62
+ require_relative "ibanity/api/webhooks/key"
63
+ require_relative "ibanity/api/webhooks/xs2a"
64
+ require_relative "ibanity/api/webhooks/ponto_connect"
54
65
 
55
66
  module Ibanity
56
67
  class << self
57
68
  def client
58
69
  options = configuration.to_h.delete_if { |_, v| v.nil? }
59
- @client ||= Ibanity::Client.new(options)
70
+ @client ||= Ibanity::Client.new(**options)
60
71
  end
61
72
 
62
73
  def configure
@@ -65,6 +76,7 @@ module Ibanity
65
76
  @isabel_connect_api_schema = nil
66
77
  @consent_api_schema = nil
67
78
  @ponto_connect_api_schema = nil
79
+ @webhooks_api_schema = nil
68
80
  @sandbox_api_schema = nil
69
81
  @configuration = nil
70
82
  yield configuration
@@ -85,8 +97,9 @@ module Ibanity
85
97
  :ponto_connect_client_secret,
86
98
  :api_scheme,
87
99
  :api_host,
88
- :api_port,
89
- :ssl_ca_file
100
+ :ssl_ca_file,
101
+ :application_id,
102
+ :debug_http_requests
90
103
  ).new
91
104
  end
92
105
 
@@ -110,6 +123,14 @@ module Ibanity
110
123
  @consent_api_schema ||= client.get(uri: "#{client.base_uri}/consent")["links"]
111
124
  end
112
125
 
126
+ def webhooks_api_schema
127
+ @webhooks_api_schema ||= client.get(uri: "#{client.base_uri}/webhooks")["links"]
128
+ end
129
+
130
+ def webhook_keys
131
+ @webhook_keys ||= Ibanity::Webhooks::Key.list
132
+ end
133
+
113
134
  def respond_to_missing?(method_name, include_private = false)
114
135
  client.respond_to?(method_name, include_private)
115
136
  end
@@ -0,0 +1,42 @@
1
+ require "ibanity"
2
+
3
+ class Ibanity::Xs2a::Car < Ibanity::BaseResource; end
4
+ class Ibanity::Xs2a::Manufacturer < Ibanity::BaseResource; end
5
+
6
+ RSpec.describe Ibanity::BaseResource do
7
+ describe "#relationship_klass" do
8
+ context "when 'data' attribute is present" do
9
+ it "retrieves the resource name from the 'type' attribute if it is present" do
10
+ car = Ibanity::Xs2a::Car.new(Fixture.load_json("relationships/data_with_type.json"))
11
+
12
+ expect(car).to respond_to(:maker)
13
+ end
14
+
15
+ it "falls back to the element name otherwise" do
16
+ car = Ibanity::Xs2a::Car.new(Fixture.load_json("relationships/data_without_type.json"))
17
+
18
+ expect(car).to respond_to(:manufacturer)
19
+ end
20
+ end
21
+
22
+ context "when the 'links' attribute is present" do
23
+ it "retrieves the resource name from the 'type' attribute within the 'links' attribute" do
24
+ car = Ibanity::Xs2a::Car.new(Fixture.load_json("relationships/meta_with_type.json"))
25
+
26
+ expect(car).to respond_to(:manufacturer)
27
+ end
28
+ end
29
+
30
+ context "when there's no 'links/related' element" do
31
+ let(:car) { Ibanity::Xs2a::Car.new(Fixture.load_json("relationships/no_links_related.json")) }
32
+
33
+ it "sets up a '<relationship_key>_id' property when there is a 'data/id' element" do
34
+ expect(car.manufacturer_id).to eq("6680437c")
35
+ end
36
+
37
+ it "discards the relationship" do
38
+ expect(car).not_to respond_to(:manufacturer)
39
+ end
40
+ end
41
+ end
42
+ end
data/spec/spec_helper.rb CHANGED
@@ -12,10 +12,16 @@
12
12
  # the additional setup, and require it from the spec files that actually need
13
13
  # it.
14
14
  #
15
+
16
+ Pathname.glob(Pathname.pwd().join("spec", "support", "**/*.rb")).each do |f|
17
+ require f
18
+ end
19
+
15
20
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
16
21
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
17
22
 
18
23
  RSpec.configure do |config|
24
+ config.include(Fixture)
19
25
  # rspec-expectations config goes here. You can use an alternate
20
26
  # assertion/expectation library such as wrong or the stdlib/minitest
21
27
  # assertions if you prefer.
@@ -0,0 +1,8 @@
1
+ require "json"
2
+
3
+ module Fixture
4
+ def self.load_json(filename)
5
+ path = Pathname([File.dirname(__FILE__ ), "fixtures", "json", filename].join("/"))
6
+ JSON.parse(File.read(path))
7
+ end
8
+ end
@@ -0,0 +1,21 @@
1
+ {
2
+ "attributes": {
3
+ "color": "blue"
4
+ },
5
+ "id": "62ebd4687e",
6
+ "links": {
7
+ "self": "https://www.cardb.com/models/62ebd4687e"
8
+ },
9
+ "relationships": {
10
+ "maker": {
11
+ "data": {
12
+ "id": "c4ebf0f7",
13
+ "type": "manufacturer"
14
+ },
15
+ "links": {
16
+ "related": "https://www.cardb.com/manufacturers/c4ebf0f7"
17
+ }
18
+ }
19
+ },
20
+ "type": "car"
21
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "attributes": {
3
+ "color": "blue"
4
+ },
5
+ "id": "62ebd4687e",
6
+ "links": {
7
+ "self": "https://www.cardb.com/models/62ebd4687e"
8
+ },
9
+ "relationships": {
10
+ "manufacturer": {
11
+ "data": {
12
+ "id": "c4ebf0f7"
13
+ },
14
+ "links": {
15
+ "related": "https://www.cardb.com/manufacturers/c4ebf0f7"
16
+ }
17
+ }
18
+ },
19
+ "type": "car"
20
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "attributes": {
3
+ "color": "blue"
4
+ },
5
+ "id": "62ebd4687e",
6
+ "links": {
7
+ "self": "https://www.cardb.com/models/62ebd4687e"
8
+ },
9
+ "relationships": {
10
+ "manufacturer": {
11
+ "links": {
12
+ "related": "https://www.cardb.com/manufacturers/c4ebf0f7"
13
+ },
14
+ "meta": {
15
+ "type": "manufacturer"
16
+ }
17
+ }
18
+ },
19
+ "type": "car"
20
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "id": "62ebd4687e",
3
+ "attributes": {
4
+ "color": "blue"
5
+ },
6
+ "relationships": {
7
+ "manufacturer": {
8
+ "data": {
9
+ "type": "manufacturer",
10
+ "id": "6680437c"
11
+ }
12
+ }
13
+ },
14
+ "type": "car"
15
+ }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ibanity
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.0
4
+ version: 1.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ibanity
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-12-04 00:00:00.000000000 Z
11
+ date: 2022-01-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rest-client
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 1.8.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: jose
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.1.3
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 1.1.3
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rspec
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -45,10 +59,11 @@ executables: []
45
59
  extensions: []
46
60
  extra_rdoc_files: []
47
61
  files:
62
+ - ".github/workflows/ci.yml"
63
+ - ".github/workflows/gem-push.yml"
48
64
  - ".gitignore"
49
65
  - ".gitkeep"
50
66
  - ".rspec"
51
- - ".travis.yml"
52
67
  - CHANGELOG.md
53
68
  - Gemfile
54
69
  - LICENSE.txt
@@ -59,6 +74,7 @@ files:
59
74
  - lib/ibanity/api/base_resource.rb
60
75
  - lib/ibanity/api/consent/consent.rb
61
76
  - lib/ibanity/api/consent/processing_operation.rb
77
+ - lib/ibanity/api/flat_resource.rb
62
78
  - lib/ibanity/api/isabel_connect/access_token.rb
63
79
  - lib/ibanity/api/isabel_connect/account.rb
64
80
  - lib/ibanity/api/isabel_connect/account_report.rb
@@ -66,12 +82,17 @@ files:
66
82
  - lib/ibanity/api/isabel_connect/bulk_payment_initiation_request.rb
67
83
  - lib/ibanity/api/isabel_connect/intraday_transaction.rb
68
84
  - lib/ibanity/api/isabel_connect/refresh_token.rb
85
+ - lib/ibanity/api/isabel_connect/token.rb
69
86
  - lib/ibanity/api/isabel_connect/transaction.rb
70
87
  - lib/ibanity/api/o_auth_resource.rb
71
88
  - lib/ibanity/api/ponto_connect/account.rb
89
+ - lib/ibanity/api/ponto_connect/bulk_payment.rb
72
90
  - lib/ibanity/api/ponto_connect/financial_institution.rb
73
91
  - lib/ibanity/api/ponto_connect/integration.rb
92
+ - lib/ibanity/api/ponto_connect/onboarding_details.rb
74
93
  - lib/ibanity/api/ponto_connect/payment.rb
94
+ - lib/ibanity/api/ponto_connect/payment_activation_request.rb
95
+ - lib/ibanity/api/ponto_connect/reauthorization_request.rb
75
96
  - lib/ibanity/api/ponto_connect/sandbox/financial_institution_account.rb
76
97
  - lib/ibanity/api/ponto_connect/sandbox/financial_institution_transaction.rb
77
98
  - lib/ibanity/api/ponto_connect/synchronization.rb
@@ -83,6 +104,9 @@ files:
83
104
  - lib/ibanity/api/sandbox/financial_institution_holding.rb
84
105
  - lib/ibanity/api/sandbox/financial_institution_transaction.rb
85
106
  - lib/ibanity/api/sandbox/financial_institution_user.rb
107
+ - lib/ibanity/api/webhooks/key.rb
108
+ - lib/ibanity/api/webhooks/ponto_connect.rb
109
+ - lib/ibanity/api/webhooks/xs2a.rb
86
110
  - lib/ibanity/api/xs2a/account.rb
87
111
  - lib/ibanity/api/xs2a/account_information_access_request.rb
88
112
  - lib/ibanity/api/xs2a/account_information_access_request_authorization.rb
@@ -103,10 +127,17 @@ files:
103
127
  - lib/ibanity/http_signature.rb
104
128
  - lib/ibanity/util.rb
105
129
  - lib/ibanity/version.rb
130
+ - lib/ibanity/webhook.rb
131
+ - spec/lib/ibanity/base_resource_spec.rb
106
132
  - spec/lib/ibanity/http_signature_spec.rb
107
133
  - spec/lib/ibanity/util_spec.rb
108
134
  - spec/spec_helper.rb
109
135
  - spec/support/crypto_helper.rb
136
+ - spec/support/fixture.rb
137
+ - spec/support/fixtures/json/relationships/data_with_type.json
138
+ - spec/support/fixtures/json/relationships/data_without_type.json
139
+ - spec/support/fixtures/json/relationships/meta_with_type.json
140
+ - spec/support/fixtures/json/relationships/no_links_related.json
110
141
  - spec/support/fixtures/signature/test-certificate.pem
111
142
  - spec/support/fixtures/signature/test-private_key.pem
112
143
  - spec/support/fixtures/signature/test-public_key.pem
@@ -129,15 +160,21 @@ required_rubygems_version: !ruby/object:Gem::Requirement
129
160
  - !ruby/object:Gem::Version
130
161
  version: '0'
131
162
  requirements: []
132
- rubygems_version: 3.0.8
163
+ rubygems_version: 3.0.3.1
133
164
  signing_key:
134
165
  specification_version: 4
135
166
  summary: Ibanity Ruby Client
136
167
  test_files:
168
+ - spec/lib/ibanity/base_resource_spec.rb
137
169
  - spec/lib/ibanity/http_signature_spec.rb
138
170
  - spec/lib/ibanity/util_spec.rb
139
171
  - spec/spec_helper.rb
140
172
  - spec/support/crypto_helper.rb
173
+ - spec/support/fixture.rb
174
+ - spec/support/fixtures/json/relationships/data_with_type.json
175
+ - spec/support/fixtures/json/relationships/data_without_type.json
176
+ - spec/support/fixtures/json/relationships/meta_with_type.json
177
+ - spec/support/fixtures/json/relationships/no_links_related.json
141
178
  - spec/support/fixtures/signature/test-certificate.pem
142
179
  - spec/support/fixtures/signature/test-private_key.pem
143
180
  - spec/support/fixtures/signature/test-public_key.pem
data/.travis.yml DELETED
@@ -1,20 +0,0 @@
1
- language: ruby
2
- script: bundle exec rspec
3
- deploy:
4
- provider: rubygems
5
- api_key:
6
- secure: pfXgGXH/AZEG3HMiaJPmJ7fck6Z3Hj0y7TU5TwvLAtKuBy6hlLT67ne/VfeBZQKa9d7eerr9PFP1hvLjZppScZMMOTtjBTvwpchkcUDwCxHx5GzS/jRnBF+uYHIza2p4Wq8yIPug7UrZ0qyq5VOG45R/nC/fPm2Ht2T7heqF5VgRaDGrN7Fq3zXn7duiYhTy02nDXorZ/kEAjnwdwDtXWyVFq8wr4inTExYrDP9/M57qx5TrkgWmcQ7JrvO2KfLDMJqAxI8lNoaPl5dAi7q1XWqqSVay7h9OAnP4UfvnvAc106lG6nHfTQig1krTfxRAvtHlPQjSlFkL+0qfBPBz1C/ex221O3NsNBYmI/mJ45lcNhqJbnkV2VEAEgUoe4ZFfKUUczLfnVxIPjoQmHMwLwfEO4g8/Hz/I3L7bvebgfzGGMDpmN6ERZRJmhutE6K5cL80ghoLs6ExJLoqHi1qvdBSkM2IyE4Yo+lob/hKU1IBL1w13T2h3EHapBh3kKn+jBzu/y5nXTEteBHbMP8j2zxFiihVBJEKPXhg/iU8WHLI6HZgWJkOc9ZH3hS7CN3VELK7fVZp9jaPAKaKihdvxQ0z3gCPFL+E6rYKsBPYqXE6qDsSQswhqTN15W0eVQqK8fDXkZmW0lSGvi2IJvL0CjIaahfB2GaapTtqOwar7sc=
7
- on:
8
- tags: true
9
- gem: ibanity
10
- skip_cleanup: true
11
- notifications:
12
- email: false
13
- slack:
14
- - rooms:
15
- - secure: J06CK+muuVTW1SS1GmhSw7ZVo0vEMFpNWU5B9Cz94h5BZI3rPSxZFSCQG8OTQ/lBIrzPTlQj5JGzYgHpS5QRHGa4s1A1tYrZUfjUUgaZ7SIfoY8AgHH6cKF+jtwYy2z+kOJekBtfNLqfzAx+48QrxdbmBnXTG+r7UXczw0TAB7BMKh8107q7kqgjKiAnpGlXzC6rLWUwdRF/1qvDtYFCjoZ0OpYMnUUK+0/bgwVHxjYRl64DrPD3jC2XoaCTRCMyLbfqb9ZUXKVf27ByAQ/Ikj+qODdSH10vwPARXlbbVhpPfjAax42zC1OwSABdE00iJW1+TuoI69znjWg1d8Ti97raihV98OXMyqHXPKxX/gZp+0MSuYIE9GQWY791ulmQ+GwwkgB6v2LmXGCE8K+eUCc3zkGJWVUzBP+Wu49mDzDAQHBPl/G3u26RqmVQzWhk9ckHQ30cyIPFjz/eaMrda+wgQ6AKzfNq+XVL6hAO+Se28PRPhDu33ZzIKqgpk0dH77LcKPI/Uf1dCT+0oa+pvVx1T0Z93SNp0azh71rx9AOWBMZm7TcOYvMOw9QpVBZkoWy4NQo9PMT0Jo73qAnOTHadr8b5zeDoAu7jnlhCxIpmnrmIh4Dz6n6XF+c+d+C/MAQVQ0rKj7KzTYEsYat0NQ62lgBtHy5/Qmu736MaIVA=
16
- if: (tag =~ ^v) AND (branch = master)
17
- - rooms:
18
- - secure: J06CK+muuVTW1SS1GmhSw7ZVo0vEMFpNWU5B9Cz94h5BZI3rPSxZFSCQG8OTQ/lBIrzPTlQj5JGzYgHpS5QRHGa4s1A1tYrZUfjUUgaZ7SIfoY8AgHH6cKF+jtwYy2z+kOJekBtfNLqfzAx+48QrxdbmBnXTG+r7UXczw0TAB7BMKh8107q7kqgjKiAnpGlXzC6rLWUwdRF/1qvDtYFCjoZ0OpYMnUUK+0/bgwVHxjYRl64DrPD3jC2XoaCTRCMyLbfqb9ZUXKVf27ByAQ/Ikj+qODdSH10vwPARXlbbVhpPfjAax42zC1OwSABdE00iJW1+TuoI69znjWg1d8Ti97raihV98OXMyqHXPKxX/gZp+0MSuYIE9GQWY791ulmQ+GwwkgB6v2LmXGCE8K+eUCc3zkGJWVUzBP+Wu49mDzDAQHBPl/G3u26RqmVQzWhk9ckHQ30cyIPFjz/eaMrda+wgQ6AKzfNq+XVL6hAO+Se28PRPhDu33ZzIKqgpk0dH77LcKPI/Uf1dCT+0oa+pvVx1T0Z93SNp0azh71rx9AOWBMZm7TcOYvMOw9QpVBZkoWy4NQo9PMT0Jo73qAnOTHadr8b5zeDoAu7jnlhCxIpmnrmIh4Dz6n6XF+c+d+C/MAQVQ0rKj7KzTYEsYat0NQ62lgBtHy5/Qmu736MaIVA=
19
- on_success: change
20
- on_failure: always