bunq-client 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/.gitignore +11 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +154 -0
- data/Rakefile +6 -0
- data/bunq-client.gemspec +41 -0
- data/lib/bunq/bunq.rb +19 -0
- data/lib/bunq/certificate_pinned.rb +21 -0
- data/lib/bunq/client.rb +197 -0
- data/lib/bunq/device_servers.rb +28 -0
- data/lib/bunq/draft_share_invite_bank.rb +13 -0
- data/lib/bunq/draft_share_invite_banks.rb +17 -0
- data/lib/bunq/installation.rb +11 -0
- data/lib/bunq/installations.rb +17 -0
- data/lib/bunq/monetary_account.rb +19 -0
- data/lib/bunq/monetary_accounts.rb +16 -0
- data/lib/bunq/paginated.rb +53 -0
- data/lib/bunq/payments.rb +17 -0
- data/lib/bunq/qr_code_content.rb +13 -0
- data/lib/bunq/resource.rb +84 -0
- data/lib/bunq/session_servers.rb +17 -0
- data/lib/bunq/signature.rb +63 -0
- data/lib/bunq/unexpected_response.rb +5 -0
- data/lib/bunq/user.rb +38 -0
- data/lib/bunq/user_company.rb +18 -0
- data/lib/bunq/version.rb +3 -0
- metadata +176 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0f110f1762a5cc428ece7acf12916702bd66dab0
|
4
|
+
data.tar.gz: 7c8c3561c1e98239994a8893e77c5394eade034f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 135f496f291fbd1796cf2fd404d9d998a06bc0a5a5fd8f1ad64c14a3f92b7be8182e3a02dea4fc93e92dafc5daee5d3ae8971461df3d595924d13789e1aa2c98
|
7
|
+
data.tar.gz: a5fb8ba159402143011cedbc90e3b64e1c85669e6d7f61949435051b65979f08d9804bc9f90ba7cbb72906525d23bd1b3ca74041cf6efb5eaba220e6d21f1ef2
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.3.2
|
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at lars.vonk@gmail.com. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: http://contributor-covenant.org
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Jortt B.V.
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
# Bunq::Client
|
2
|
+
|
3
|
+
The `Bunq::Client` is a ruby wrapper of the [bunq Public API](https://doc.bunq.com) extracted from [Jortt Online boekhouden](https://www.jortt.nl).
|
4
|
+
|
5
|
+
The `Bunq::Client` is the main interface which can be used to navigate to other resources. The response objects are just
|
6
|
+
hashes similar to the documentation of the bunq API. Only the content of `Response` is returned.
|
7
|
+
|
8
|
+
The bunq [Pagination](https://doc.bunq.com/api/1/page/pagination) is implemented using a ruby `Enumerator`.
|
9
|
+
All `index` methods are `Paginated`. This means you can loop through large resultsets without loading
|
10
|
+
everything into memory.
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
Add this line to your application's Gemfile:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
gem 'bunq-client'
|
18
|
+
```
|
19
|
+
|
20
|
+
And then execute:
|
21
|
+
|
22
|
+
$ bundle
|
23
|
+
|
24
|
+
Or install it yourself as:
|
25
|
+
|
26
|
+
$ gem install bunq-client
|
27
|
+
|
28
|
+
### One time setup
|
29
|
+
|
30
|
+
1) Create installation
|
31
|
+
```ruby
|
32
|
+
|
33
|
+
# Generate keys on the fly. Make sure you save these keys in a secure location.
|
34
|
+
# Alternatively use keys already generated.
|
35
|
+
openssl_key = OpenSSL::PKey::RSA.new(2048)
|
36
|
+
public_key = openssl_key.public_key.to_pem
|
37
|
+
private_key = openssl_key.to_pem
|
38
|
+
|
39
|
+
Bunq.configure do |config|
|
40
|
+
config.api_key = 'YOUR API KEY'
|
41
|
+
config.private_key = private_key
|
42
|
+
config.server_public_key = nil # you don't have this yet
|
43
|
+
cofig.installation_token = nil # this MUST be nil for creating installations
|
44
|
+
end
|
45
|
+
|
46
|
+
# Create the installation
|
47
|
+
installation = Bunq.client.installations.create(public_key)
|
48
|
+
|
49
|
+
# Print the installation token to put in your Bunq::Configuration
|
50
|
+
installation_token = installation[1]['Token']['token']
|
51
|
+
puts "config.installation_token = #{installation_token}"
|
52
|
+
|
53
|
+
# Keep the public key to put in your Bunq::Configuration
|
54
|
+
server_public_key_location = "./server_public_key.pub"
|
55
|
+
File.open(server_public_key_location, 'w') { |file| file.write(installation[2]['ServerPublicKey']['server_public_key']) }
|
56
|
+
puts "config.server_public_key written to file #{server_public_key_location}"
|
57
|
+
```
|
58
|
+
|
59
|
+
2) Register your device as device server
|
60
|
+
This is typically your application server (or your laptop when playing around).
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
Bunq.configure do |config|
|
64
|
+
config.api_key = 'YOUR API KEY'
|
65
|
+
# Used for request signing
|
66
|
+
config.private_key = 'SAME PRIVATE KEY AS IN STEP 1'
|
67
|
+
# Used for response verification
|
68
|
+
config.server_public_key = 'THE CONTENTS OF THE PUBLIC KEY FILE RETURNED IN STEP 1'
|
69
|
+
cofig.installation_token = 'THE INSTALLATION TOKEN RETURNED IN STEP 1'
|
70
|
+
end
|
71
|
+
|
72
|
+
response = Bunq.client.device_servers.create('My Laptop')
|
73
|
+
puts "Device server created: #{response[0]['Id']['id']}"
|
74
|
+
```
|
75
|
+
|
76
|
+
3) Optional: Pin certificate (if you want to receive callbacks)
|
77
|
+
```ruby
|
78
|
+
Bunq.configure do |config|
|
79
|
+
config.api_key = 'YOUR API KEY'
|
80
|
+
# Used for request signing
|
81
|
+
config.private_key = 'SAME PRIVATE KEY AS IN STEP 1'
|
82
|
+
# Used for response verification
|
83
|
+
config.server_public_key = 'THE CONTENTS OF THE PUBLIC KEY FILE RETURNED IN STEP 1'
|
84
|
+
cofig.installation_token = 'THE INSTALLATION TOKEN RETURNED IN STEP 1'
|
85
|
+
end
|
86
|
+
|
87
|
+
certificate_of_you_callback_url = IO.read('path_to_pem_file')
|
88
|
+
Bunq.client.me_as_user.certificate_pinned.create(certificate_of_you_callback_url)
|
89
|
+
```
|
90
|
+
|
91
|
+
4) Optional: Register callback url (for realtime updates of e.g. payments)
|
92
|
+
```ruby
|
93
|
+
Bunq.configure do |config|
|
94
|
+
config.api_key = 'YOUR API KEY'
|
95
|
+
# Used for request signing
|
96
|
+
config.private_key = 'SAME PRIVATE KEY AS IN STEP 1'
|
97
|
+
# Used for response verification
|
98
|
+
config.server_public_key = 'THE CONTENTS OF THE PUBLIC KEY FILE RETURNED IN STEP 1'
|
99
|
+
cofig.installation_token = 'THE INSTALLATION TOKEN RETURNED IN STEP 1'
|
100
|
+
end
|
101
|
+
|
102
|
+
Bunq.client.me_as_user_company.update(
|
103
|
+
notification_filters: [{
|
104
|
+
"notification_delivery_method": "URL",
|
105
|
+
"notification_target": 'https://YOUR_CALLBACK_URL',
|
106
|
+
"category": "PAYMENT"
|
107
|
+
}]
|
108
|
+
)
|
109
|
+
```
|
110
|
+
|
111
|
+
## Usage
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
Bunq.configure do |config|
|
115
|
+
|
116
|
+
# Mandatory configuration after inital setup phase
|
117
|
+
config.api_key = 'YOUR API KEY'
|
118
|
+
# Private key used for request signing
|
119
|
+
config.private_key = 'YOUR PRIVATE KEY'
|
120
|
+
# Public key from bunq retrieved via Bunq.client.installations.create
|
121
|
+
config.server_public_key = 'SERVER PUBLIC KEY'
|
122
|
+
# Installation token retrieved via Bunq.client.installations.create
|
123
|
+
config.installation_token = 'YOUR INSTALLATION TOKEN'
|
124
|
+
|
125
|
+
|
126
|
+
# Optional configuration for access to the sandbox
|
127
|
+
# config.sandbox = true
|
128
|
+
# if config.sandbox
|
129
|
+
# config.sandbox_user = 'USER'
|
130
|
+
# config.sandbox_password = 'PASSWORD'
|
131
|
+
# end
|
132
|
+
end
|
133
|
+
|
134
|
+
# List id's of all your monetary_accounts
|
135
|
+
Bunq.client.me_as_user.monetary_accounts.index.each do |monetary_account|
|
136
|
+
puts monetary_account['MonetaryAccountBank']['id']
|
137
|
+
end
|
138
|
+
```
|
139
|
+
|
140
|
+
## Development
|
141
|
+
|
142
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
143
|
+
|
144
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
145
|
+
|
146
|
+
## Contributing
|
147
|
+
|
148
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/jorttbv/bunq-client. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
149
|
+
|
150
|
+
|
151
|
+
## License
|
152
|
+
|
153
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
154
|
+
|
data/Rakefile
ADDED
data/bunq-client.gemspec
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'bunq/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "bunq-client"
|
8
|
+
spec.version = Bunq::VERSION
|
9
|
+
spec.authors = [
|
10
|
+
'Lars Vonk',
|
11
|
+
'Bob Forma',
|
12
|
+
'Derek Kraan',
|
13
|
+
]
|
14
|
+
spec.email = [
|
15
|
+
'lars.vonk@gmail.com',
|
16
|
+
'bforma@zilverline.com',
|
17
|
+
'dkraan@zilverline.com',
|
18
|
+
]
|
19
|
+
|
20
|
+
spec.summary = %q{Ruby client for the bunq public API.}
|
21
|
+
spec.description = %q{www.jortt.nl}
|
22
|
+
spec.homepage = "https://www.jortt.nl"
|
23
|
+
spec.license = "MIT"
|
24
|
+
|
25
|
+
|
26
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
27
|
+
f.match(%r{^(test|spec|features)/})
|
28
|
+
end
|
29
|
+
spec.bindir = "exe"
|
30
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
31
|
+
spec.require_paths = ["lib"]
|
32
|
+
|
33
|
+
spec.add_runtime_dependency 'rest-client', '~> 2.0'
|
34
|
+
|
35
|
+
spec.add_development_dependency "bundler", "~> 1.13"
|
36
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
37
|
+
spec.add_development_dependency "rspec", "~> 3.5"
|
38
|
+
spec.add_development_dependency "webmock", "~> 2.3.2"
|
39
|
+
spec.add_development_dependency "rspec-json_expectations", "~> 2.1"
|
40
|
+
spec.add_development_dependency "codecov", "~> 0.1.10"
|
41
|
+
end
|
data/lib/bunq/bunq.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rest-client'
|
2
|
+
require_relative './client'
|
3
|
+
require_relative './signature'
|
4
|
+
require_relative 'paginated'
|
5
|
+
|
6
|
+
RestClient.add_before_execution_proc do |req, params|
|
7
|
+
next unless params[:url].include?('bunq.com')
|
8
|
+
req['X-Bunq-Client-Request-Id'] = params[:headers][:'X-Bunq-Client-Request-Id'] = SecureRandom.uuid
|
9
|
+
|
10
|
+
# can't sign the creation of an installation
|
11
|
+
# see https://doc.bunq.com/api/1/call/installation/method/post
|
12
|
+
next if params[:url].end_with?('/installation') && req.method == 'POST'
|
13
|
+
req['X-Bunq-Client-Signature'] = Bunq.signature.create(
|
14
|
+
params[:method].upcase,
|
15
|
+
req.path,
|
16
|
+
params[:headers],
|
17
|
+
params[:payload]
|
18
|
+
)
|
19
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Bunq
|
2
|
+
##
|
3
|
+
# https://doc.bunq.com/api/1/call/certificate-pinned
|
4
|
+
class CertificatePinned
|
5
|
+
def initialize(parent_resource)
|
6
|
+
@resource = parent_resource.append("/certificate-pinned")
|
7
|
+
end
|
8
|
+
|
9
|
+
##
|
10
|
+
# https://doc.bunq.com/api/1/call/certificate-pinned/method/post
|
11
|
+
def create(pem_certificate)
|
12
|
+
@resource.with_session do
|
13
|
+
@resource.post({
|
14
|
+
certificate_chain: [
|
15
|
+
{certificate: pem_certificate}
|
16
|
+
]
|
17
|
+
})
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/bunq/client.rb
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
require_relative './version'
|
5
|
+
require_relative './resource'
|
6
|
+
|
7
|
+
require_relative './installations'
|
8
|
+
require_relative './installation'
|
9
|
+
require_relative './device_servers'
|
10
|
+
require_relative './session_servers'
|
11
|
+
require_relative './user'
|
12
|
+
require_relative './user_company'
|
13
|
+
require_relative './monetary_account'
|
14
|
+
require_relative './monetary_accounts'
|
15
|
+
require_relative './payments'
|
16
|
+
require_relative './signature'
|
17
|
+
|
18
|
+
##
|
19
|
+
# Usage
|
20
|
+
#
|
21
|
+
# Bunq.configure do |config|
|
22
|
+
# config.api_key = 'YOUR_APIKEY'
|
23
|
+
# config.installation_token = 'YOUR_INSTALLATION_TOKEN'
|
24
|
+
# config.private_key = 'YOUR PRIVATE KEY'
|
25
|
+
# config.server_public_key = 'SERVER PUBLIC KEY'
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# client = Bunq.client
|
29
|
+
# number_of_accounts = client.me_as_user.monetary_accounts.index.to_a.count
|
30
|
+
# puts "User has #{number_of_accounts} accounts"
|
31
|
+
#
|
32
|
+
module Bunq
|
33
|
+
class << self
|
34
|
+
attr_accessor :configuration
|
35
|
+
|
36
|
+
def configure
|
37
|
+
self.configuration ||= Configuration.new
|
38
|
+
yield(configuration)
|
39
|
+
|
40
|
+
configuration.base_url = Configuration::SANDBOX_BASE_URL if configuration.sandbox
|
41
|
+
|
42
|
+
fail 'api_key is mandatory' unless self.configuration.api_key
|
43
|
+
end
|
44
|
+
|
45
|
+
##
|
46
|
+
# Returns a new instance of +Client+ with the current +configuration+.
|
47
|
+
#
|
48
|
+
def client
|
49
|
+
fail "No configuration! Call Bunq.configure first." unless configuration
|
50
|
+
Client.new(configuration)
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# Returns a new instance of +Signature+
|
55
|
+
#
|
56
|
+
def signature
|
57
|
+
fail "No configuration! Call Bunq.configure first." unless configuration
|
58
|
+
Signature.new(configuration.private_key, configuration.server_public_key)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
##
|
63
|
+
# Configuration object for connecting to the bunq api
|
64
|
+
#
|
65
|
+
class Configuration
|
66
|
+
SANDBOX_BASE_URL = 'https://sandbox.public.api.bunq.com'
|
67
|
+
PRODUCTION_BASE_URL = 'https://api.bunq.com'
|
68
|
+
|
69
|
+
DEFAULT_LANGUAGE = 'nl_NL'
|
70
|
+
DEFAULT_REGION = 'nl_NL'
|
71
|
+
DEFAULT_GEOLOCATION = '0 0 0 0 000'
|
72
|
+
DEFAULT_USER_AGENT = "bunq ruby client #{Bunq::VERSION}"
|
73
|
+
|
74
|
+
# Base url for the bunq api. Defaults to +PRODUCTION_BASE_URL+
|
75
|
+
attr_accessor :base_url,
|
76
|
+
# Flag to set to connect to sandbox. Defaults to +false+.
|
77
|
+
# If set to +true+ you must also specify +sandbox_user+
|
78
|
+
# and +sandbox_password+
|
79
|
+
:sandbox,
|
80
|
+
# The username for connecting to the sandbox
|
81
|
+
:sandbox_user,
|
82
|
+
# The password for connecting to the sandbox
|
83
|
+
:sandbox_password,
|
84
|
+
# Your installation token obtained from bunq
|
85
|
+
:installation_token,
|
86
|
+
# Your api key obtained from bunq
|
87
|
+
:api_key,
|
88
|
+
# Your language. Defaults to +DEFAULT_LANGUAGE+
|
89
|
+
:language,
|
90
|
+
# Your region. Defaults to +DEFAULT_REGION+
|
91
|
+
:region,
|
92
|
+
# Your geolocation. Defaults to +DEFAULT_GEOLOCATION+
|
93
|
+
:geolocation,
|
94
|
+
# Arbitrary user agent to connect to bunq. Defaults to +DEFAULT_USER_AGENT+
|
95
|
+
:user_agent,
|
96
|
+
# Flag to set when you want to disable the signature
|
97
|
+
# retrieved from bunq. Mainly useful for testing.
|
98
|
+
# Defaults to +false+
|
99
|
+
:disable_response_signature_verification,
|
100
|
+
# The private key for signing the request
|
101
|
+
:private_key,
|
102
|
+
# The public key of this installation for verifying the response
|
103
|
+
:server_public_key
|
104
|
+
|
105
|
+
|
106
|
+
def initialize
|
107
|
+
@sandbox = false
|
108
|
+
@base_url = PRODUCTION_BASE_URL
|
109
|
+
@language = DEFAULT_LANGUAGE
|
110
|
+
@region = DEFAULT_REGION
|
111
|
+
@geolocation = DEFAULT_GEOLOCATION
|
112
|
+
@user_agent = DEFAULT_USER_AGENT
|
113
|
+
@disable_response_signature_verification = false
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
##
|
118
|
+
# The Bunq::Client is the adapter for the Bunq Public Api (doc.bunq.com)
|
119
|
+
#
|
120
|
+
# An instance of a +Client+ can be obtained via +Bunq.client+
|
121
|
+
class Client
|
122
|
+
|
123
|
+
attr_accessor :current_session
|
124
|
+
attr_reader :configuration
|
125
|
+
|
126
|
+
def initialize(configuration)
|
127
|
+
@configuration = configuration
|
128
|
+
end
|
129
|
+
|
130
|
+
def installations
|
131
|
+
Bunq::Installations.new(self)
|
132
|
+
end
|
133
|
+
|
134
|
+
def installation(id)
|
135
|
+
Bunq::Installation.new(self, id)
|
136
|
+
end
|
137
|
+
|
138
|
+
def device_servers
|
139
|
+
Bunq::DeviceServers.new(self)
|
140
|
+
end
|
141
|
+
|
142
|
+
def session_servers
|
143
|
+
Bunq::SessionServers.new(self)
|
144
|
+
end
|
145
|
+
|
146
|
+
def user(id)
|
147
|
+
Bunq::User.new(self, id)
|
148
|
+
end
|
149
|
+
|
150
|
+
def user_company(id)
|
151
|
+
Bunq::UserCompany.new(self, id)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Returns the +Bunq::UserCompany+ represented by the +Bunq::Configuration.api_key+
|
155
|
+
def me_as_user_company
|
156
|
+
with_session { user_company(current_session_user_id) }
|
157
|
+
end
|
158
|
+
|
159
|
+
# Returns the +Bunq::User+ represented by the +Bunq::Configuration.api_key+
|
160
|
+
def me_as_user
|
161
|
+
with_session { user(current_session_user_id) }
|
162
|
+
end
|
163
|
+
|
164
|
+
def ensure_session!
|
165
|
+
@current_session ||= session_servers.create
|
166
|
+
end
|
167
|
+
|
168
|
+
def with_session(&block)
|
169
|
+
ensure_session!
|
170
|
+
block.call
|
171
|
+
end
|
172
|
+
|
173
|
+
def headers
|
174
|
+
{
|
175
|
+
'Accept': 'application/json',
|
176
|
+
'Cache-Control': 'no-cache',
|
177
|
+
'Content-Type': 'application/json',
|
178
|
+
'User-Agent': configuration.user_agent,
|
179
|
+
'X-Bunq-Language': configuration.language,
|
180
|
+
'X-Bunq-Geolocation': configuration.geolocation,
|
181
|
+
'X-Bunq-Region': configuration.region,
|
182
|
+
}.tap do |h|
|
183
|
+
if configuration.installation_token
|
184
|
+
h[:'X-Bunq-Client-Authentication'] = configuration.installation_token
|
185
|
+
end
|
186
|
+
|
187
|
+
if current_session
|
188
|
+
h[:'X-Bunq-Client-Authentication'] = current_session[1]['Token']['token']
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def current_session_user_id
|
194
|
+
current_session[2].first[1]['id']
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Bunq
|
2
|
+
##
|
3
|
+
# See https://doc.bunq.com/api/1/call/device-server
|
4
|
+
class DeviceServers
|
5
|
+
|
6
|
+
##
|
7
|
+
# +client+ an instance of +Bunq::Client+
|
8
|
+
#
|
9
|
+
def initialize(client)
|
10
|
+
@resource = Bunq::Resource.new(client, "/v1/device-server")
|
11
|
+
@client = client
|
12
|
+
end
|
13
|
+
|
14
|
+
##
|
15
|
+
# https://doc.bunq.com/api/1/call/device-server/method/post
|
16
|
+
def create(description)
|
17
|
+
fail ArgumentError.new('description is required') unless description
|
18
|
+
|
19
|
+
@resource.post(description: description, secret: @client.configuration.api_key)['Response']
|
20
|
+
end
|
21
|
+
|
22
|
+
##
|
23
|
+
# https://doc.bunq.com/api/1/call/device-server/method/list
|
24
|
+
def index
|
25
|
+
@resource.get['Response']
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative 'qr_code_content'
|
2
|
+
|
3
|
+
module Bunq
|
4
|
+
class DraftShareInviteBank
|
5
|
+
def initialize(parent_resource, id)
|
6
|
+
@resource = parent_resource.append("/draft-share-invite-bank/#{id}")
|
7
|
+
end
|
8
|
+
|
9
|
+
def qr_code_content
|
10
|
+
Bunq::QrCodeContent.new(@resource)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require_relative 'qr_code_content'
|
2
|
+
|
3
|
+
module Bunq
|
4
|
+
class DraftShareInviteBanks
|
5
|
+
def initialize(parent_resource)
|
6
|
+
@resource = parent_resource.append("/draft-share-invite-bank")
|
7
|
+
end
|
8
|
+
|
9
|
+
def create(invite)
|
10
|
+
@resource.with_session { @resource.post(invite) }['Response']
|
11
|
+
end
|
12
|
+
|
13
|
+
def index
|
14
|
+
@resource.with_session { @resource.get }['Response']
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Bunq
|
2
|
+
class Installations
|
3
|
+
def initialize(client)
|
4
|
+
@resource = Bunq::Resource.new(client, "/v1/installation")
|
5
|
+
end
|
6
|
+
|
7
|
+
def create(public_key)
|
8
|
+
fail ArgumentError.new('public_key is required') unless public_key
|
9
|
+
|
10
|
+
@resource.post({client_public_key: public_key}, true)['Response']
|
11
|
+
end
|
12
|
+
|
13
|
+
def index
|
14
|
+
@resource.get['Response']
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Bunq
|
2
|
+
##
|
3
|
+
# https://doc.bunq.com/api/1/call/monetary-account
|
4
|
+
class MonetaryAccount
|
5
|
+
def initialize(parent_resource, id)
|
6
|
+
@resource = parent_resource.append("/monetary-account/#{id}")
|
7
|
+
end
|
8
|
+
|
9
|
+
def payments
|
10
|
+
Bunq::Payments.new(@resource)
|
11
|
+
end
|
12
|
+
|
13
|
+
##
|
14
|
+
# https://doc.bunq.com/api/1/call/monetary-account/method/get
|
15
|
+
def show
|
16
|
+
@resource.with_session { @resource.get }['Response']
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Bunq
|
2
|
+
##
|
3
|
+
# https://doc.bunq.com/api/1/call/monetary-account
|
4
|
+
class MonetaryAccounts
|
5
|
+
def initialize(parent_resource)
|
6
|
+
@resource = parent_resource.append("/monetary-account")
|
7
|
+
end
|
8
|
+
|
9
|
+
# https://doc.bunq.com/api/1/call/monetary-account-bank/method/list
|
10
|
+
def index(count: 200, older_id: nil, newer_id: nil)
|
11
|
+
Bunq::Paginated
|
12
|
+
.new(@resource)
|
13
|
+
.paginate(count: count, older_id: older_id, newer_id: newer_id)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require_relative 'unexpected_response'
|
2
|
+
|
3
|
+
module Bunq
|
4
|
+
class MissingPaginationObject < UnexpectedResponse; end
|
5
|
+
# https://doc.bunq.com/api/1/page/pagination
|
6
|
+
class Paginated
|
7
|
+
def initialize(resource)
|
8
|
+
@resource = resource
|
9
|
+
end
|
10
|
+
|
11
|
+
def paginate(count: 200, older_id: nil, newer_id: nil)
|
12
|
+
params = setup_params(count, older_id, newer_id)
|
13
|
+
@resource.with_session { enumerator(params) }
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
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
|
20
|
+
|
21
|
+
params = {count: count}
|
22
|
+
params[:older_id] = older_id if older_id
|
23
|
+
params[:newer_id] = newer_id if newer_id
|
24
|
+
params
|
25
|
+
end
|
26
|
+
|
27
|
+
def enumerator(params)
|
28
|
+
last_page = false
|
29
|
+
next_params = params
|
30
|
+
|
31
|
+
Enumerator.new do |yielder|
|
32
|
+
loop do
|
33
|
+
raise StopIteration if last_page
|
34
|
+
|
35
|
+
result = @resource.get(next_params)
|
36
|
+
result['Response'].each do |item|
|
37
|
+
yielder << item
|
38
|
+
end
|
39
|
+
|
40
|
+
pagination = result['Pagination']
|
41
|
+
fail MissingPaginationObject unless pagination
|
42
|
+
|
43
|
+
last_page = !pagination['older_url']
|
44
|
+
next_params = params.merge(older_id: param('older_id', pagination['older_url'])) unless last_page
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def param(name, url)
|
50
|
+
CGI.parse(URI(url).query)[name]&.first
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require_relative 'paginated'
|
2
|
+
|
3
|
+
module Bunq
|
4
|
+
# https://doc.bunq.com/api/1/call/payment
|
5
|
+
class Payments
|
6
|
+
def initialize(parent_resource)
|
7
|
+
@resource = parent_resource.append("/payment")
|
8
|
+
end
|
9
|
+
|
10
|
+
# https://doc.bunq.com/api/1/call/payment/method/list
|
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
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require_relative 'signature'
|
2
|
+
require_relative 'unexpected_response'
|
3
|
+
require 'restclient'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module Bunq
|
7
|
+
class Resource
|
8
|
+
attr_reader :resource
|
9
|
+
|
10
|
+
def initialize(client, path)
|
11
|
+
@client = client
|
12
|
+
@path = path
|
13
|
+
@resource = RestClient::Resource.new(
|
14
|
+
"#{client.configuration.base_url}#{path}",
|
15
|
+
{
|
16
|
+
headers: client.headers
|
17
|
+
}.tap do |x|
|
18
|
+
if client.configuration.sandbox
|
19
|
+
x[:user] = client.configuration.sandbox_user
|
20
|
+
x[:password] = client.configuration.sandbox_password
|
21
|
+
end
|
22
|
+
end
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
def get(params = {}, &block)
|
27
|
+
@resource.get(params: params) do |response, request, result|
|
28
|
+
verify_and_handle_response(response, request, result, &block)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def post(payload, skip_verify = false, &block)
|
33
|
+
if skip_verify
|
34
|
+
@resource.post(JSON.generate(payload)) do |response, request, result|
|
35
|
+
handle_response(response, request, result, &block)
|
36
|
+
end
|
37
|
+
else
|
38
|
+
@resource.post(JSON.generate(payload)) do |response, request, result|
|
39
|
+
verify_and_handle_response(response, request, result, &block)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def put(payload, &block)
|
45
|
+
@resource.put(JSON.generate(payload)) do |response, request, result|
|
46
|
+
verify_and_handle_response(response, request, result, &block)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def append(path)
|
51
|
+
Bunq::Resource.new(client, @path + path)
|
52
|
+
end
|
53
|
+
|
54
|
+
def ensure_session!
|
55
|
+
client.ensure_session!
|
56
|
+
end
|
57
|
+
|
58
|
+
def with_session(&block)
|
59
|
+
client.with_session(&block)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
attr_reader :client
|
65
|
+
|
66
|
+
def verify_and_handle_response(response, request, result, &block)
|
67
|
+
Bunq.signature.verify!(response) unless client.configuration.disable_response_signature_verification
|
68
|
+
handle_response(response, request, result, &block)
|
69
|
+
end
|
70
|
+
|
71
|
+
def handle_response(response, _request, _result, &block)
|
72
|
+
case response.code
|
73
|
+
when 200, 201
|
74
|
+
if block_given?
|
75
|
+
yield(response)
|
76
|
+
else
|
77
|
+
JSON.parse(response.body)
|
78
|
+
end
|
79
|
+
else
|
80
|
+
fail UnexpectedResponse.new(code: response.code, headers: response.raw_headers, body: response.body)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Bunq
|
2
|
+
##
|
3
|
+
# https://doc.bunq.com/api/1/call/session-server
|
4
|
+
class SessionServers
|
5
|
+
def initialize(client)
|
6
|
+
@resource = Bunq::Resource.new(client, "/v1/session-server")
|
7
|
+
@api_key = client.configuration.api_key
|
8
|
+
end
|
9
|
+
|
10
|
+
##
|
11
|
+
# https://doc.bunq.com/api/1/call/session-server/method/post
|
12
|
+
def create
|
13
|
+
fail 'Cannot create session, please provide api_key to Bunq::Client' unless @api_key
|
14
|
+
@resource.post(secret: @api_key)['Response']
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require_relative 'unexpected_response'
|
2
|
+
|
3
|
+
module Bunq
|
4
|
+
|
5
|
+
class Signature
|
6
|
+
# headers in raw_headers hash in rest client are all lower case
|
7
|
+
BUNQ_HEADER_PREFIX = 'X-Bunq-'.downcase
|
8
|
+
BUNQ_SERVER_SIGNATURE_RESPONSE_HEADER = 'X-Bunq-Server-Signature'.downcase
|
9
|
+
CACHE_CONTROL_HEADER = 'Cache-Control'.downcase
|
10
|
+
USER_AGENT_HEADER = 'User-Agent'.downcase
|
11
|
+
SIGNABLE_HEADERS = [CACHE_CONTROL_HEADER, USER_AGENT_HEADER]
|
12
|
+
|
13
|
+
def initialize(private_key, server_public_key)
|
14
|
+
fail ArgumentError.new('private_key is mandatory') unless private_key
|
15
|
+
fail ArgumentError.new('server_public_key is mandatory') unless server_public_key
|
16
|
+
|
17
|
+
@private_key = OpenSSL::PKey::RSA.new(private_key)
|
18
|
+
@server_public_key = OpenSSL::PKey::RSA.new(server_public_key)
|
19
|
+
end
|
20
|
+
|
21
|
+
def create(verb, path, headers, body)
|
22
|
+
signature = private_key.sign(digest, signable_input(verb, path, headers.select { |header_name, _| signable_header?(header_name) }, body))
|
23
|
+
Base64.strict_encode64(signature)
|
24
|
+
end
|
25
|
+
|
26
|
+
def verify!(response)
|
27
|
+
sorted_bunq_headers = response.raw_headers.select(&method(:verifiable_header?)).sort.to_h.map { |k, v| "#{k.to_s.split('-').map(&:capitalize).join('-')}: #{v.first}" }
|
28
|
+
data = %Q{#{response.code}\n#{sorted_bunq_headers.join("\n")}\n\n#{response.body}}
|
29
|
+
|
30
|
+
signature_headers = response.raw_headers.find { |k, _| k.to_s.downcase == BUNQ_SERVER_SIGNATURE_RESPONSE_HEADER }[1]
|
31
|
+
fail UnexpectedResponse.new(code: response.code, headers: response.raw_headers, body: response.body) unless signature_headers
|
32
|
+
|
33
|
+
signature = Base64.strict_decode64(signature_headers.first)
|
34
|
+
fail UnexpectedResponse.new(code: response.code, headers: response.raw_headers, body: response.body) unless server_public_key.verify(digest, signature, data)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
attr_reader :private_key, :server_public_key
|
40
|
+
|
41
|
+
def digest
|
42
|
+
OpenSSL::Digest::SHA256.new
|
43
|
+
end
|
44
|
+
|
45
|
+
def signable_input(verb, path, headers, body)
|
46
|
+
head = [
|
47
|
+
[verb, path].join(' '),
|
48
|
+
headers.sort.to_h.map { |k,v| "#{k}: #{v}" }.join("\n")
|
49
|
+
].join("\n")
|
50
|
+
"#{head}\n\n#{body}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def signable_header?(header_name)
|
54
|
+
_header_name = header_name.to_s.downcase
|
55
|
+
SIGNABLE_HEADERS.include?(_header_name) || _header_name.start_with?(BUNQ_HEADER_PREFIX)
|
56
|
+
end
|
57
|
+
|
58
|
+
def verifiable_header?(header_name, _)
|
59
|
+
_header_name = header_name.to_s.downcase
|
60
|
+
_header_name.start_with?(BUNQ_HEADER_PREFIX) && _header_name != BUNQ_SERVER_SIGNATURE_RESPONSE_HEADER
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/bunq/user.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require_relative 'resource'
|
2
|
+
require_relative 'monetary_account'
|
3
|
+
require_relative 'monetary_accounts'
|
4
|
+
require_relative 'draft_share_invite_bank'
|
5
|
+
require_relative 'draft_share_invite_banks'
|
6
|
+
require_relative 'certificate_pinned'
|
7
|
+
|
8
|
+
module Bunq
|
9
|
+
class User
|
10
|
+
def initialize(client, id)
|
11
|
+
@resource = Bunq::Resource.new(client, "/v1/user/#{id}")
|
12
|
+
end
|
13
|
+
|
14
|
+
def monetary_account(id)
|
15
|
+
Bunq::MonetaryAccount.new(@resource, id)
|
16
|
+
end
|
17
|
+
|
18
|
+
def monetary_accounts
|
19
|
+
Bunq::MonetaryAccounts.new(@resource)
|
20
|
+
end
|
21
|
+
|
22
|
+
def draft_share_invite_bank(id)
|
23
|
+
Bunq::DraftShareInviteBank.new(@resource, id)
|
24
|
+
end
|
25
|
+
|
26
|
+
def draft_share_invite_banks
|
27
|
+
Bunq::DraftShareInviteBanks.new(@resource)
|
28
|
+
end
|
29
|
+
|
30
|
+
def certificate_pinned
|
31
|
+
Bunq::CertificatePinned.new(@resource)
|
32
|
+
end
|
33
|
+
|
34
|
+
def show
|
35
|
+
@resource.with_session { @resource.get }['Response']
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative 'resource'
|
2
|
+
require_relative 'draft_share_invite_bank'
|
3
|
+
|
4
|
+
module Bunq
|
5
|
+
class UserCompany
|
6
|
+
def initialize(client, id)
|
7
|
+
@resource = Bunq::Resource.new(client, "/v1/user-company/#{id}")
|
8
|
+
end
|
9
|
+
|
10
|
+
def show
|
11
|
+
@resource.with_session { @resource.get }['Response']
|
12
|
+
end
|
13
|
+
|
14
|
+
def update(user_company)
|
15
|
+
@resource.with_session { @resource.put(user_company) }['Response']
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/bunq/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bunq-client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Lars Vonk
|
8
|
+
- Bob Forma
|
9
|
+
- Derek Kraan
|
10
|
+
autorequire:
|
11
|
+
bindir: exe
|
12
|
+
cert_chain: []
|
13
|
+
date: 2017-04-28 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rest-client
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - "~>"
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2.0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - "~>"
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '2.0'
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: bundler
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - "~>"
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '1.13'
|
36
|
+
type: :development
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - "~>"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '1.13'
|
43
|
+
- !ruby/object:Gem::Dependency
|
44
|
+
name: rake
|
45
|
+
requirement: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '10.0'
|
50
|
+
type: :development
|
51
|
+
prerelease: false
|
52
|
+
version_requirements: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - "~>"
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '10.0'
|
57
|
+
- !ruby/object:Gem::Dependency
|
58
|
+
name: rspec
|
59
|
+
requirement: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - "~>"
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '3.5'
|
64
|
+
type: :development
|
65
|
+
prerelease: false
|
66
|
+
version_requirements: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - "~>"
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '3.5'
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: webmock
|
73
|
+
requirement: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - "~>"
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 2.3.2
|
78
|
+
type: :development
|
79
|
+
prerelease: false
|
80
|
+
version_requirements: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - "~>"
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: 2.3.2
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
name: rspec-json_expectations
|
87
|
+
requirement: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - "~>"
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '2.1'
|
92
|
+
type: :development
|
93
|
+
prerelease: false
|
94
|
+
version_requirements: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - "~>"
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '2.1'
|
99
|
+
- !ruby/object:Gem::Dependency
|
100
|
+
name: codecov
|
101
|
+
requirement: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - "~>"
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: 0.1.10
|
106
|
+
type: :development
|
107
|
+
prerelease: false
|
108
|
+
version_requirements: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - "~>"
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: 0.1.10
|
113
|
+
description: www.jortt.nl
|
114
|
+
email:
|
115
|
+
- lars.vonk@gmail.com
|
116
|
+
- bforma@zilverline.com
|
117
|
+
- dkraan@zilverline.com
|
118
|
+
executables: []
|
119
|
+
extensions: []
|
120
|
+
extra_rdoc_files: []
|
121
|
+
files:
|
122
|
+
- ".gitignore"
|
123
|
+
- ".rspec"
|
124
|
+
- ".ruby-version"
|
125
|
+
- ".travis.yml"
|
126
|
+
- CODE_OF_CONDUCT.md
|
127
|
+
- Gemfile
|
128
|
+
- LICENSE.txt
|
129
|
+
- README.md
|
130
|
+
- Rakefile
|
131
|
+
- bunq-client.gemspec
|
132
|
+
- lib/bunq/bunq.rb
|
133
|
+
- lib/bunq/certificate_pinned.rb
|
134
|
+
- lib/bunq/client.rb
|
135
|
+
- lib/bunq/device_servers.rb
|
136
|
+
- lib/bunq/draft_share_invite_bank.rb
|
137
|
+
- lib/bunq/draft_share_invite_banks.rb
|
138
|
+
- lib/bunq/installation.rb
|
139
|
+
- lib/bunq/installations.rb
|
140
|
+
- lib/bunq/monetary_account.rb
|
141
|
+
- lib/bunq/monetary_accounts.rb
|
142
|
+
- lib/bunq/paginated.rb
|
143
|
+
- lib/bunq/payments.rb
|
144
|
+
- lib/bunq/qr_code_content.rb
|
145
|
+
- lib/bunq/resource.rb
|
146
|
+
- lib/bunq/session_servers.rb
|
147
|
+
- lib/bunq/signature.rb
|
148
|
+
- lib/bunq/unexpected_response.rb
|
149
|
+
- lib/bunq/user.rb
|
150
|
+
- lib/bunq/user_company.rb
|
151
|
+
- lib/bunq/version.rb
|
152
|
+
homepage: https://www.jortt.nl
|
153
|
+
licenses:
|
154
|
+
- MIT
|
155
|
+
metadata: {}
|
156
|
+
post_install_message:
|
157
|
+
rdoc_options: []
|
158
|
+
require_paths:
|
159
|
+
- lib
|
160
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
161
|
+
requirements:
|
162
|
+
- - ">="
|
163
|
+
- !ruby/object:Gem::Version
|
164
|
+
version: '0'
|
165
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
166
|
+
requirements:
|
167
|
+
- - ">="
|
168
|
+
- !ruby/object:Gem::Version
|
169
|
+
version: '0'
|
170
|
+
requirements: []
|
171
|
+
rubyforge_project:
|
172
|
+
rubygems_version: 2.5.2
|
173
|
+
signing_key:
|
174
|
+
specification_version: 4
|
175
|
+
summary: Ruby client for the bunq public API.
|
176
|
+
test_files: []
|