bunq-client 0.7.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rspec.yml +40 -0
  3. data/.rubocop.yml +212 -0
  4. data/.ruby-version +1 -1
  5. data/CHANGELOG.md +4 -0
  6. data/Gemfile +2 -0
  7. data/README.md +2 -0
  8. data/Rakefile +7 -3
  9. data/bunq-client.gemspec +22 -20
  10. data/lib/bunq/attachment.rb +15 -0
  11. data/lib/bunq/attachment_public.rb +13 -0
  12. data/lib/bunq/attachment_public_content.rb +4 -2
  13. data/lib/bunq/attachment_publics.rb +20 -0
  14. data/lib/bunq/attachments.rb +20 -0
  15. data/lib/bunq/avatar.rb +15 -0
  16. data/lib/bunq/avatars.rb +15 -0
  17. data/lib/bunq/bunq.rb +2 -0
  18. data/lib/bunq/bunqme_tab.rb +17 -0
  19. data/lib/bunq/bunqme_tabs.rb +21 -0
  20. data/lib/bunq/card.rb +2 -0
  21. data/lib/bunq/cards.rb +3 -1
  22. data/lib/bunq/certificate_pinned.rb +10 -6
  23. data/lib/bunq/client.rb +74 -51
  24. data/lib/bunq/device_servers.rb +5 -4
  25. data/lib/bunq/draft_share_invite_bank.rb +2 -0
  26. data/lib/bunq/draft_share_invite_banks.rb +3 -1
  27. data/lib/bunq/encryptor.rb +6 -7
  28. data/lib/bunq/errors.rb +9 -2
  29. data/lib/bunq/header.rb +20 -0
  30. data/lib/bunq/installation.rb +2 -0
  31. data/lib/bunq/installations.rb +4 -2
  32. data/lib/bunq/monetary_account.rb +17 -0
  33. data/lib/bunq/monetary_account_bank.rb +17 -0
  34. data/lib/bunq/monetary_account_banks.rb +21 -0
  35. data/lib/bunq/monetary_accounts.rb +3 -1
  36. data/lib/bunq/notification_filter_url.rb +4 -2
  37. data/lib/bunq/paginated.rb +14 -3
  38. data/lib/bunq/payment.rb +2 -0
  39. data/lib/bunq/payments.rb +3 -1
  40. data/lib/bunq/qr_code_content.rb +5 -3
  41. data/lib/bunq/resource.rb +25 -11
  42. data/lib/bunq/session_servers.rb +5 -2
  43. data/lib/bunq/signature.rb +9 -7
  44. data/lib/bunq/user.rb +17 -0
  45. data/lib/bunq/user_company.rb +2 -0
  46. data/lib/bunq/user_person.rb +2 -0
  47. data/lib/bunq/users.rb +17 -0
  48. data/lib/bunq/version.rb +3 -1
  49. metadata +53 -20
  50. data/.travis.yml +0 -5
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bunq
4
+ module Header
5
+ ACCEPT = 'Accept'
6
+ ATTACHMENT_DESCRIPTION = 'X-Bunq-Attachment-Description'
7
+ CACHE_CONTROL = 'Cache-Control'
8
+ CONTENT_TYPE = 'Content-Type'
9
+ CLIENT_AUTH = 'X-Bunq-Client-Authentication'
10
+ CLIENT_ENCRYPTION_HMAC = 'X-Bunq-Client-Encryption-Hmac'
11
+ CLIENT_ENCRYPTION_IV = 'X-Bunq-Client-Encryption-Iv'
12
+ CLIENT_ENCRYPTION_KEY = 'X-Bunq-Client-Encryption-Key'
13
+ CLIENT_REQUEST_ID = 'X-Bunq-Client-Request-Id'
14
+ CLIENT_SIGNATURE = 'X-Bunq-Client-Signature'
15
+ GEOLOCATION = 'X-Bunq-Geolocation'
16
+ LANGUAGE = 'X-Bunq-Language'
17
+ REGION = 'X-Bunq-Region'
18
+ USER_AGENT = 'User-Agent'
19
+ end
20
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bunq
2
4
  class Installation
3
5
  def initialize(client, id)
@@ -1,11 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bunq
2
4
  class Installations
3
5
  def initialize(client)
4
- @resource = Bunq::Resource.new(client, "/v1/installation")
6
+ @resource = Bunq::Resource.new(client, '/v1/installation')
5
7
  end
6
8
 
7
9
  def create(public_key)
8
- fail ArgumentError.new('public_key is required') unless public_key
10
+ fail ArgumentError, 'public_key is required' unless public_key
9
11
 
10
12
  @resource.post({client_public_key: public_key}, skip_verify: true)['Response']
11
13
  end
@@ -1,4 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'attachments'
1
4
  require_relative 'notification_filter_url'
5
+ require_relative 'bunqme_tab'
6
+ require_relative 'bunqme_tabs'
2
7
 
3
8
  module Bunq
4
9
  ##
@@ -8,6 +13,18 @@ module Bunq
8
13
  @resource = parent_resource.append("/monetary-account/#{id}")
9
14
  end
10
15
 
16
+ def attachments
17
+ Bunq::Attachments.new(@resource)
18
+ end
19
+
20
+ def bunqme_tab(id)
21
+ Bunq::BunqmeTab.new(@resource, id)
22
+ end
23
+
24
+ def bunqme_tabs
25
+ Bunq::BunqmeTabs.new(@resource)
26
+ end
27
+
11
28
  def payment(id)
12
29
  Bunq::Payment.new(@resource, id)
13
30
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bunq
4
+ class MonetaryAccountBank
5
+ def initialize(parent_resource, id)
6
+ @resource = parent_resource.append("/monetary-account-bank/#{id}")
7
+ end
8
+
9
+ def show
10
+ @resource.with_session { @resource.get }['Response']
11
+ end
12
+
13
+ def update(attributes)
14
+ @resource.with_session { @resource.put(attributes) }['Response']
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'paginated'
4
+
5
+ module Bunq
6
+ class MonetaryAccountBanks
7
+ def initialize(parent_resource)
8
+ @resource = parent_resource.append('/monetary-account-bank')
9
+ end
10
+
11
+ def index(count: 200, older_id: nil, newer_id: nil)
12
+ Bunq::Paginated
13
+ .new(@resource)
14
+ .paginate(count: count, older_id: older_id, newer_id: newer_id)
15
+ end
16
+
17
+ def create(attributes)
18
+ @resource.with_session { @resource.post(attributes) }['Response']
19
+ end
20
+ end
21
+ end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bunq
2
4
  ##
3
5
  # https://doc.bunq.com/api/1/call/monetary-account
4
6
  class MonetaryAccounts
5
7
  def initialize(parent_resource)
6
- @resource = parent_resource.append("/monetary-account")
8
+ @resource = parent_resource.append('/monetary-account')
7
9
  end
8
10
 
9
11
  # https://doc.bunq.com/api/1/call/monetary-account-bank/method/list
@@ -1,12 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bunq
2
4
  # https://doc.bunq.com/#/notification-filter-url
3
5
  class NotificationFilterUrl
4
6
  def initialize(parent_resource)
5
- @resource = parent_resource.append("/notification-filter-url")
7
+ @resource = parent_resource.append('/notification-filter-url')
6
8
  end
7
9
 
8
10
  def create(notification_filters)
9
- @resource.with_session { @resource.post({notification_filters: notification_filters})}['Response']
11
+ @resource.with_session { @resource.post({notification_filters: notification_filters}) }['Response']
10
12
  end
11
13
 
12
14
  def show
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'errors'
2
4
 
3
5
  module Bunq
@@ -16,7 +18,7 @@ module Bunq
16
18
  private
17
19
 
18
20
  def setup_params(count, older_id, newer_id)
19
- fail ArgumentError.new('Cant pass both older_id and newer_id') if older_id && newer_id
21
+ fail ArgumentError, 'Cant pass both older_id and newer_id' if older_id && newer_id
20
22
 
21
23
  params = {count: count}
22
24
  params[:older_id] = older_id if older_id
@@ -30,7 +32,7 @@ module Bunq
30
32
 
31
33
  Enumerator.new do |yielder|
32
34
  loop do
33
- raise StopIteration if last_page
35
+ fail StopIteration if last_page
34
36
 
35
37
  result = @resource.with_session { @resource.get(next_params) }
36
38
  result['Response'].each do |item|
@@ -41,18 +43,27 @@ module Bunq
41
43
  fail MissingPaginationObject unless pagination
42
44
 
43
45
  last_page = !pagination[paging_url(params)]
44
- next_params = params.merge(:"#{paging_id(params)}" => param(paging_id(params), pagination[paging_url(params)])) unless last_page
46
+ next if last_page
47
+
48
+ next_params = params.merge(
49
+ "#{paging_id(params)}": param(
50
+ paging_id(params),
51
+ pagination[paging_url(params)],
52
+ ),
53
+ )
45
54
  end
46
55
  end
47
56
  end
48
57
 
49
58
  def paging_url(params)
50
59
  return 'newer_url' if params[:newer_id]
60
+
51
61
  'older_url'
52
62
  end
53
63
 
54
64
  def paging_id(params)
55
65
  return 'newer_id' if params[:newer_id]
66
+
56
67
  'older_id'
57
68
  end
58
69
 
data/lib/bunq/payment.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bunq
2
4
  # https://doc.bunq.com/api/1/call/payment
3
5
  class Payment
data/lib/bunq/payments.rb CHANGED
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'paginated'
2
4
 
3
5
  module Bunq
4
6
  # https://doc.bunq.com/api/1/call/payment
5
7
  class Payments
6
8
  def initialize(parent_resource)
7
- @resource = parent_resource.append("/payment")
9
+ @resource = parent_resource.append('/payment')
8
10
  end
9
11
 
10
12
  # https://doc.bunq.com/api/1/call/payment/method/list
@@ -1,12 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bunq
2
4
  class QrCodeContent
3
5
  def initialize(parent_resource)
4
- @resource = parent_resource.append("/qr-code-content")
6
+ @resource = parent_resource.append('/qr-code-content')
5
7
  end
6
8
 
7
9
  def show
8
- @resource.with_session do
9
- @resource.get { |response| response.body }
10
+ @resource.with_session do
11
+ @resource.get(&:body)
10
12
  end
11
13
  end
12
14
  end
data/lib/bunq/resource.rb CHANGED
@@ -1,11 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'errors'
2
4
  require 'restclient'
3
5
  require 'json'
4
6
 
5
7
  module Bunq
6
8
  class Resource
7
- attr_reader :resource
8
- NO_PARAMS = {}
9
+ APPLICATION_JSON = 'application/json'
10
+
11
+ NO_PARAMS = {}.freeze
9
12
 
10
13
  def initialize(client, path)
11
14
  @client = client
@@ -20,12 +23,13 @@ module Bunq
20
23
  raise Bunq::Timeout
21
24
  end
22
25
 
23
-
24
- def post(payload, skip_verify: false, encrypt: false, &block)
25
- body = JSON.generate(payload)
26
+ def post(payload, skip_verify: false, encrypt: false, custom_headers: {}, &block)
27
+ custom_headers = JSON.parse(custom_headers.to_json)
28
+ body = post_body(payload, custom_headers)
26
29
  body, headers = client.encryptor.encrypt(body) if encrypt
30
+ headers = headers.to_h.merge(custom_headers)
27
31
 
28
- headers = bunq_request_headers('POST', NO_PARAMS, body, headers || {})
32
+ headers = bunq_request_headers('POST', NO_PARAMS, body, headers)
29
33
 
30
34
  resource.post(body, headers) do |response, request, result|
31
35
  if skip_verify
@@ -74,26 +78,27 @@ module Bunq
74
78
  x[:user] = client.configuration.sandbox_user
75
79
  x[:password] = client.configuration.sandbox_password
76
80
  end
77
- end
81
+ end,
78
82
  )
79
83
  end
80
84
 
81
85
  def bunq_request_headers(verb, params, payload = nil, headers = {})
82
- headers['X-Bunq-Client-Request-Id'] = SecureRandom.uuid
86
+ headers[Bunq::Header::CLIENT_REQUEST_ID] = SecureRandom.uuid
83
87
 
84
88
  unless @path.end_with?('/installation') && verb == 'POST'
85
- headers['X-Bunq-Client-Signature'] = sign_request(verb, params, headers, payload)
89
+ headers[Bunq::Header::CLIENT_SIGNATURE] = sign_request(verb, params, headers, payload)
86
90
  end
87
91
 
88
92
  headers
89
93
  end
90
94
 
91
- def sign_request(verb, params, headers, payload = nil)
95
+ def sign_request(_verb, _params, _headers, payload = nil)
92
96
  client.signature.create(payload)
93
97
  end
94
98
 
95
99
  def encode_params(path, params)
96
100
  return path if params.empty?
101
+
97
102
  "#{path}?#{URI.escape(params.collect { |k, v| "#{k}=#{v}" }.join('&'))}"
98
103
  end
99
104
 
@@ -109,7 +114,7 @@ module Bunq
109
114
  (100..499).include?(response.code)
110
115
  end
111
116
 
112
- def handle_response(response, _request, _result, &block)
117
+ def handle_response(response, _request, _result)
113
118
  if response.code == 200 || response.code == 201
114
119
  if block_given?
115
120
  yield(response)
@@ -128,5 +133,14 @@ module Bunq
128
133
  fail UnexpectedResponse.new(code: response.code, headers: response.raw_headers, body: response.body)
129
134
  end
130
135
  end
136
+
137
+ def post_body(payload, custom_headers)
138
+ if custom_headers.key?(Bunq::Header::CONTENT_TYPE) &&
139
+ custom_headers[Bunq::Header::CONTENT_TYPE] != APPLICATION_JSON
140
+ payload
141
+ else
142
+ JSON.generate(payload)
143
+ end
144
+ end
131
145
  end
132
146
  end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bunq
2
4
  ##
3
5
  # https://doc.bunq.com/api/1/call/session-server
4
6
  class SessionServers
5
7
  def initialize(client)
6
- @resource = Bunq::Resource.new(client, "/v1/session-server")
8
+ @resource = Bunq::Resource.new(client, '/v1/session-server')
7
9
  @api_key = client.configuration.api_key
8
10
  end
9
11
 
@@ -11,7 +13,8 @@ module Bunq
11
13
  # https://doc.bunq.com/api/1/call/session-server/method/post
12
14
  def create
13
15
  fail 'Cannot create session, please add the api_key to your configuration' unless @api_key
14
- @resource.post(secret: @api_key)['Response']
16
+
17
+ @resource.post({secret: @api_key})['Response']
15
18
  end
16
19
  end
17
20
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'errors'
2
4
 
3
5
  module Bunq
@@ -7,8 +9,8 @@ module Bunq
7
9
  BUNQ_SERVER_SIGNATURE_RESPONSE_HEADER = 'X-Bunq-Server-Signature'.downcase
8
10
 
9
11
  def initialize(private_key, server_public_key)
10
- fail ArgumentError.new('private_key is mandatory') unless private_key
11
- fail ArgumentError.new('server_public_key is mandatory') unless server_public_key
12
+ fail ArgumentError, 'private_key is mandatory' unless private_key
13
+ fail ArgumentError, 'server_public_key is mandatory' unless server_public_key
12
14
 
13
15
  @private_key = OpenSSL::PKey::RSA.new(private_key)
14
16
  @server_public_key = OpenSSL::PKey::RSA.new(server_public_key)
@@ -48,12 +50,12 @@ module Bunq
48
50
  end
49
51
 
50
52
  def verifiable_header?(header_name, _)
51
- _header_name = header_name.to_s.downcase
52
- _header_name.start_with?(BUNQ_HEADER_PREFIX) && _header_name != BUNQ_SERVER_SIGNATURE_RESPONSE_HEADER
53
+ the_header_name = header_name.to_s.downcase
54
+ the_header_name.start_with?(BUNQ_HEADER_PREFIX) && the_header_name != BUNQ_SERVER_SIGNATURE_RESPONSE_HEADER
53
55
  end
54
56
 
55
- def skip_signature_check(responseCode)
56
- (Bunq::configuration.sandbox && responseCode == 409) || responseCode == 429
57
+ def skip_signature_check(response_code)
58
+ (Bunq.configuration.sandbox && response_code == 409) || response_code == 429
57
59
  end
58
60
 
59
61
  def verify_legacy(signature, response)
@@ -66,7 +68,7 @@ module Bunq
66
68
  "#{k.to_s.split('-').map(&:capitalize).join('-')}: #{v.first}"
67
69
  end
68
70
 
69
- verify(signature, %Q{#{response.code}\n#{sorted_bunq_headers.join("\n")}\n\n#{response.body}})
71
+ verify(signature, %(#{response.code}\n#{sorted_bunq_headers.join("\n")}\n\n#{response.body}))
70
72
  end
71
73
 
72
74
  def verify_modern(signature, response)
data/lib/bunq/user.rb CHANGED
@@ -1,12 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'resource'
2
4
  require_relative 'monetary_account'
3
5
  require_relative 'monetary_accounts'
6
+ require_relative 'monetary_account_bank'
7
+ require_relative 'monetary_account_banks'
4
8
  require_relative 'draft_share_invite_bank'
5
9
  require_relative 'draft_share_invite_banks'
6
10
  require_relative 'certificate_pinned'
7
11
  require_relative 'card'
8
12
  require_relative 'cards'
9
13
  require_relative 'notification_filter_url'
14
+ require_relative 'attachment'
10
15
 
11
16
  module Bunq
12
17
  class User
@@ -14,6 +19,10 @@ module Bunq
14
19
  @resource = Bunq::Resource.new(client, "/v1/user/#{id}")
15
20
  end
16
21
 
22
+ def attachment(id)
23
+ Bunq::Attachment.new(@resource, id)
24
+ end
25
+
17
26
  def monetary_account(id)
18
27
  Bunq::MonetaryAccount.new(@resource, id)
19
28
  end
@@ -22,6 +31,14 @@ module Bunq
22
31
  Bunq::MonetaryAccounts.new(@resource)
23
32
  end
24
33
 
34
+ def monetary_account_bank(id)
35
+ Bunq::MonetaryAccountBank.new(@resource, id)
36
+ end
37
+
38
+ def monetary_account_banks
39
+ Bunq::MonetaryAccountBanks.new(@resource)
40
+ end
41
+
25
42
  def draft_share_invite_bank(id)
26
43
  Bunq::DraftShareInviteBank.new(@resource, id)
27
44
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'resource'
2
4
  require_relative 'draft_share_invite_bank'
3
5