nubank_sdk 0.4.1 → 0.5.1
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 +4 -4
- data/.github/workflows/gem-push.yml +31 -0
- data/.rubocop.yml +5 -0
- data/.vscode/settings.json +3 -1
- data/Gemfile +1 -1
- data/Gemfile.lock +43 -3
- data/README.md +39 -1
- data/Rakefile +20 -0
- data/certificates/.gitkeep +0 -0
- data/lib/nubank_sdk/account.rb +22 -34
- data/lib/nubank_sdk/api_routes.rb +39 -14
- data/lib/nubank_sdk/auth.rb +103 -19
- data/lib/nubank_sdk/certificate.rb +44 -9
- data/lib/nubank_sdk/client.rb +52 -13
- data/lib/nubank_sdk/user.rb +64 -0
- data/lib/nubank_sdk/version.rb +1 -1
- data/nubank_sdk.gemspec +5 -2
- metadata +64 -6
- data/usage_example.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 88e935ce5affb735e7676f0dd93a213658308ce205f837028172bac24d0ceb0e
|
4
|
+
data.tar.gz: dc9bd5c2cc72d965a2cfa0bb20e81eb3e43e6f4077806cfcab04532c1db00181
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7a3217000244778e41aa31f56df110888cedae14efcdcd39b88a7e6fc430bb53bac67eee8a9570a0a67de8035a9d7e2f892bc9413c65e3198772ebe92b4039b9
|
7
|
+
data.tar.gz: bf2509cbf0b73934c5e490fb146e499c286b658ab4050417ac47f9b17b7dc851829c31bbf9a58c265c00a9ff27714d0d3ba8ddf063186b17562e11c618d6da09
|
@@ -0,0 +1,31 @@
|
|
1
|
+
name: Ruby Gem
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ "main" ]
|
6
|
+
|
7
|
+
jobs:
|
8
|
+
build:
|
9
|
+
name: Build + Publish
|
10
|
+
runs-on: ubuntu-latest
|
11
|
+
permissions:
|
12
|
+
contents: read
|
13
|
+
packages: write
|
14
|
+
|
15
|
+
steps:
|
16
|
+
- uses: actions/checkout@v3
|
17
|
+
- name: Set up Ruby 2.6
|
18
|
+
uses: actions/setup-ruby@v1
|
19
|
+
with:
|
20
|
+
ruby-version: 2.6.x
|
21
|
+
|
22
|
+
- name: Publish to RubyGems
|
23
|
+
run: |
|
24
|
+
mkdir -p $HOME/.gem
|
25
|
+
touch $HOME/.gem/credentials
|
26
|
+
chmod 0600 $HOME/.gem/credentials
|
27
|
+
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
28
|
+
gem build *.gemspec
|
29
|
+
gem push *.gem
|
30
|
+
env:
|
31
|
+
GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
|
data/.rubocop.yml
ADDED
data/.vscode/settings.json
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,18 +1,36 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
nubank_sdk (0.
|
4
|
+
nubank_sdk (0.5.1)
|
5
5
|
faraday (~> 0.15.0)
|
6
|
-
json (~> 2.
|
6
|
+
json (~> 2.3)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
+
activesupport (5.2.8.1)
|
12
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
13
|
+
i18n (>= 0.7, < 2)
|
14
|
+
minitest (~> 5.1)
|
15
|
+
tzinfo (~> 1.1)
|
16
|
+
ast (2.4.2)
|
17
|
+
concurrent-ruby (1.1.10)
|
11
18
|
diff-lcs (1.5.0)
|
19
|
+
factory_bot (4.8.2)
|
20
|
+
activesupport (>= 3.0.0)
|
12
21
|
faraday (0.15.4)
|
13
22
|
multipart-post (>= 1.2, < 3)
|
14
|
-
|
23
|
+
i18n (1.12.0)
|
24
|
+
concurrent-ruby (~> 1.0)
|
25
|
+
jaro_winkler (1.5.4)
|
26
|
+
json (2.6.1)
|
27
|
+
minitest (5.15.0)
|
15
28
|
multipart-post (2.2.3)
|
29
|
+
parallel (1.19.2)
|
30
|
+
parser (3.1.2.1)
|
31
|
+
ast (~> 2.4.1)
|
32
|
+
powerpack (0.1.3)
|
33
|
+
rainbow (3.1.1)
|
16
34
|
rake (10.5.0)
|
17
35
|
rspec (3.11.0)
|
18
36
|
rspec-core (~> 3.11.0)
|
@@ -27,15 +45,37 @@ GEM
|
|
27
45
|
diff-lcs (>= 1.2.0, < 2.0)
|
28
46
|
rspec-support (~> 3.11.0)
|
29
47
|
rspec-support (3.11.1)
|
48
|
+
rubocop (0.57.2)
|
49
|
+
jaro_winkler (~> 1.5.1)
|
50
|
+
parallel (~> 1.10)
|
51
|
+
parser (>= 2.5)
|
52
|
+
powerpack (~> 0.1)
|
53
|
+
rainbow (>= 2.2.2, < 4.0)
|
54
|
+
ruby-progressbar (~> 1.7)
|
55
|
+
unicode-display_width (~> 1.0, >= 1.0.1)
|
56
|
+
rubocop-rspec (1.27.0)
|
57
|
+
rubocop (>= 0.56.0)
|
58
|
+
ruby-progressbar (1.11.0)
|
59
|
+
thread_safe (0.3.6)
|
60
|
+
tzinfo (1.2.10)
|
61
|
+
thread_safe (~> 0.1)
|
62
|
+
unicode-display_width (1.8.0)
|
63
|
+
webrick (1.7.0)
|
64
|
+
yard (0.9.28)
|
65
|
+
webrick (~> 1.7.0)
|
30
66
|
|
31
67
|
PLATFORMS
|
32
68
|
ruby
|
33
69
|
|
34
70
|
DEPENDENCIES
|
35
71
|
bundler (~> 1.17)
|
72
|
+
factory_bot (~> 4.8.2)
|
36
73
|
nubank_sdk!
|
37
74
|
rake (~> 10.0)
|
38
75
|
rspec (~> 3.0)
|
76
|
+
rubocop (~> 0.57.2)
|
77
|
+
rubocop-rspec (~> 1.27.0)
|
78
|
+
yard (~> 0.9.12)
|
39
79
|
|
40
80
|
BUNDLED WITH
|
41
81
|
1.17.3
|
data/README.md
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+

|
2
|
+
|
1
3
|
# NubankSdk (Work in progress)
|
2
4
|
|
3
5
|
A gem to make it ease to monitorize your Nubank account.
|
@@ -22,7 +24,43 @@ Or install it yourself as:
|
|
22
24
|
|
23
25
|
## Usage
|
24
26
|
|
25
|
-
|
27
|
+
```ruby
|
28
|
+
require 'nubank_sdk'
|
29
|
+
|
30
|
+
# instance a nubank account object
|
31
|
+
user = NubankSdk::User.new(cpf: '12345678909')
|
32
|
+
password = 'dracarys'
|
33
|
+
```
|
34
|
+
|
35
|
+
> First time?
|
36
|
+
>
|
37
|
+
> authenticate the account
|
38
|
+
>
|
39
|
+
> ```ruby
|
40
|
+
> # request an email code
|
41
|
+
> account_email = user.auth.request_email_code(password)
|
42
|
+
>
|
43
|
+
> # get the email code from the user
|
44
|
+
> puts "Enter the code sent to #{account_email}: "
|
45
|
+
> email_code = gets.chomp
|
46
|
+
> user.auth.exchange_certs(email_code, password)
|
47
|
+
> ```
|
48
|
+
>
|
49
|
+
---
|
50
|
+
>
|
51
|
+
> Has a certificate?
|
52
|
+
>
|
53
|
+
> generate a access token
|
54
|
+
>
|
55
|
+
> ```ruby
|
56
|
+
> user.auth.authenticate_with_certificate(password)
|
57
|
+
> ```
|
58
|
+
|
59
|
+
get the account balance
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
user.account.balance # => 77.0
|
63
|
+
```
|
26
64
|
|
27
65
|
## Development
|
28
66
|
|
data/Rakefile
CHANGED
@@ -2,5 +2,25 @@ require "bundler/gem_tasks"
|
|
2
2
|
require "rspec/core/rake_task"
|
3
3
|
|
4
4
|
RSpec::Core::RakeTask.new(:spec)
|
5
|
+
Bundler::GemHelper.install_tasks
|
5
6
|
|
6
7
|
task :default => :spec
|
8
|
+
|
9
|
+
task :start_new_release do
|
10
|
+
# TODO: add guard clean
|
11
|
+
bump = ENV['BUMP'] || 'patch'
|
12
|
+
|
13
|
+
sh 'gem install gem-release'
|
14
|
+
sh "gem bump --version #{bump}"
|
15
|
+
|
16
|
+
Rake::Task[:build].invoke
|
17
|
+
|
18
|
+
sh 'git add .'
|
19
|
+
sh "git commit -m \"build(version): :bookmark: bump #{bump}\""
|
20
|
+
sh 'git push'
|
21
|
+
|
22
|
+
version = NubankSdk::VERSION
|
23
|
+
version_tag = "v#{version}"
|
24
|
+
sh "git tag -a #{version_tag} -m \"Version #{version}\""
|
25
|
+
sh 'git push --tags'
|
26
|
+
end
|
data/certificates/.gitkeep
CHANGED
File without changes
|
data/lib/nubank_sdk/account.rb
CHANGED
@@ -1,43 +1,31 @@
|
|
1
1
|
module NubankSdk
|
2
2
|
class Account
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
@
|
10
|
-
@
|
11
|
-
@key = key
|
3
|
+
#
|
4
|
+
# Returns the account statement
|
5
|
+
#
|
6
|
+
# @param [NubankSdk::Client::HTTPS] connection
|
7
|
+
# @param [NubankSdk::ApiRoutes] api_routes
|
8
|
+
def initialize(connection:, api_routes:)
|
9
|
+
@connection = connection
|
10
|
+
@api_routes = api_routes
|
12
11
|
end
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
13
|
+
#
|
14
|
+
# Returns the account balance
|
15
|
+
#
|
16
|
+
# @return [Float]
|
17
|
+
def balance
|
18
|
+
query_url = @api_routes.entrypoint(path: :ssl, entrypoint: :query)
|
19
|
+
|
20
|
+
response = @connection.post(
|
21
|
+
query_url, {
|
22
|
+
'variables': {},
|
23
|
+
'query': '{viewer {savingsAccount {currentSavingsBalance {netAmount}}}}'
|
24
|
+
}
|
21
25
|
)
|
22
|
-
end
|
23
|
-
|
24
|
-
def account_balance
|
25
|
-
query_url = auth.api_routes.entrypoint(path: :ssl, entrypoint: :query)
|
26
|
-
connection = Client::HTTPS.new(auth.certificate.encoded, @adapter)
|
27
26
|
|
28
|
-
|
29
|
-
'variables': {},
|
30
|
-
'query': '{viewer {savingsAccount {currentSavingsBalance {netAmount}}}}'
|
31
|
-
}, { Authorization: "Bearer #{auth.access_token}" })
|
32
|
-
|
33
|
-
data = JSON.parse(response.body, symbolize_names: true)
|
27
|
+
data = NubankSdk::Client.get_body(response)
|
34
28
|
data[:data][:viewer][:savingsAccount][:currentSavingsBalance][:netAmount]
|
35
29
|
end
|
36
|
-
|
37
|
-
private
|
38
|
-
|
39
|
-
def generate_device_id
|
40
|
-
SecureRandom.uuid.split('-').last
|
41
|
-
end
|
42
30
|
end
|
43
|
-
end
|
31
|
+
end
|
@@ -1,37 +1,52 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
require 'faraday'
|
4
2
|
require 'json'
|
5
3
|
|
6
4
|
module NubankSdk
|
7
5
|
class ApiRoutes
|
8
|
-
DISCOVERY_URI =
|
6
|
+
DISCOVERY_URI = 'https://prod-s0-webapp-proxy.nubank.com.br'.freeze
|
9
7
|
PROXY_PATHS = {
|
10
8
|
default: '/api/discovery',
|
11
9
|
app: '/api/app/discovery',
|
12
|
-
ssl: ''
|
13
|
-
}
|
10
|
+
ssl: ''
|
11
|
+
}.freeze
|
14
12
|
|
13
|
+
#
|
14
|
+
# Controller for the ApiRoutes class
|
15
|
+
#
|
16
|
+
# @param [Hash] url_discovery_map
|
17
|
+
# @param [[Symbol, Faraday::Adapter::Test::Stubs]] connection_adapter
|
15
18
|
def initialize(url_discovery_map: {}, connection_adapter: nil)
|
16
19
|
@url_discovery_map = url_discovery_map
|
17
20
|
@connection_adapter = connection_adapter
|
18
21
|
end
|
19
22
|
|
20
|
-
#
|
23
|
+
#
|
24
|
+
# Return the url for a given path and entrypoint
|
25
|
+
#
|
26
|
+
# @param [Symbol] path
|
27
|
+
# @param [Symbol] entrypoint
|
28
|
+
# @param [Symbol] type, :splitted or :full
|
29
|
+
#
|
30
|
+
# @return [String, Array]
|
21
31
|
def entrypoint(path: :default, entrypoint:, type: :full)
|
22
32
|
discovery(path) if @url_discovery_map[path].nil?
|
23
33
|
|
24
34
|
url = @url_discovery_map[path][entrypoint]
|
25
|
-
|
26
|
-
if type == :full
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
return [url_splitted.first, "/api#{url_splitted.last}"]
|
32
|
-
end
|
35
|
+
|
36
|
+
return url if type == :full
|
37
|
+
|
38
|
+
url_splitted = url.split('/api')
|
39
|
+
[url_splitted.first, "/api#{url_splitted.last}"]
|
33
40
|
end
|
34
41
|
|
42
|
+
#
|
43
|
+
# Add new entrypoint to url discovery map
|
44
|
+
#
|
45
|
+
# @param [Symbol] path
|
46
|
+
# @param [Symbol] entrypoint
|
47
|
+
# @param [String] url
|
48
|
+
#
|
49
|
+
# @return [Hash]
|
35
50
|
def add_entrypoint(path: :default, entrypoint:, url:)
|
36
51
|
path_map = @url_discovery_map[path] || {}
|
37
52
|
path_map[entrypoint] = url
|
@@ -41,6 +56,12 @@ module NubankSdk
|
|
41
56
|
|
42
57
|
private
|
43
58
|
|
59
|
+
# @!visibility private
|
60
|
+
# Request to the nubank api to get the url discovery map
|
61
|
+
#
|
62
|
+
# @param [Symbol] path
|
63
|
+
#
|
64
|
+
# @return [Hash]
|
44
65
|
def discovery(path = :default)
|
45
66
|
return @url_discovery_map[path] if @url_discovery_map[path]
|
46
67
|
|
@@ -50,6 +71,10 @@ module NubankSdk
|
|
50
71
|
@url_discovery_map[path] = url_map
|
51
72
|
end
|
52
73
|
|
74
|
+
# @!visibility private
|
75
|
+
# Return a default connection with the nubank api
|
76
|
+
#
|
77
|
+
# @return [Client::HTTP]
|
53
78
|
def connection
|
54
79
|
@connection ||= Client::HTTP.new(DISCOVERY_URI, @connection_adapter)
|
55
80
|
end
|
data/lib/nubank_sdk/auth.rb
CHANGED
@@ -1,26 +1,36 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
module NubankSdk
|
4
2
|
class Auth
|
5
3
|
attr_reader :refresh_token, :refresh_before, :access_token
|
6
4
|
|
7
|
-
|
5
|
+
#
|
6
|
+
# Auth method to connect with the nubank api
|
7
|
+
#
|
8
|
+
# @param [String] cpf the cpf to authenticate
|
9
|
+
# @param [String] device_id the device id to authenticate
|
10
|
+
# @param [NubankSdk::ApiRoutes] api_routes the api routes to connect
|
11
|
+
# @param [[Symbol, Faraday::Adapter::Test::Stubs]] adapter the adapter to connect
|
12
|
+
def initialize(cpf:, device_id: nil, api_routes: nil, connection_adapter: nil)
|
8
13
|
@cpf = cpf
|
9
|
-
@device_id = device_id
|
10
|
-
@key = key || generate_key
|
14
|
+
@device_id = device_id || generate_device_id
|
11
15
|
@api_routes = api_routes || NubankSdk::ApiRoutes.new
|
12
16
|
|
13
|
-
@
|
14
|
-
end
|
15
|
-
|
16
|
-
def api_routes
|
17
|
-
@api_routes
|
17
|
+
@connection_adapter = connection_adapter
|
18
18
|
end
|
19
19
|
|
20
|
+
#
|
21
|
+
# Return the instance of user certificate
|
22
|
+
#
|
23
|
+
# @return [NubankSdk::Certificate] the certificate instance
|
20
24
|
def certificate
|
21
|
-
@certificate ||= NubankSdk::Certificate.new(@cpf
|
25
|
+
@certificate ||= NubankSdk::Certificate.new(@cpf)
|
22
26
|
end
|
23
27
|
|
28
|
+
#
|
29
|
+
# Authenticate with the nubank api to get a new access token
|
30
|
+
#
|
31
|
+
# @param [String] password the password to authenticate
|
32
|
+
#
|
33
|
+
# @return [NubankSdk::ApiRoutes] the api routes with the new links
|
24
34
|
def authenticate_with_certificate(password)
|
25
35
|
token_url = @api_routes.entrypoint(path: :app, entrypoint: :token)
|
26
36
|
response = ssl_connection.post(token_url, token_payload(password))
|
@@ -34,6 +44,12 @@ module NubankSdk
|
|
34
44
|
update_api_routes(response_hash[:_links])
|
35
45
|
end
|
36
46
|
|
47
|
+
#
|
48
|
+
# Request to nubank api to generate a new certificate
|
49
|
+
#
|
50
|
+
# @param [String] password the password to authenticate
|
51
|
+
#
|
52
|
+
# @return [String] email was has been received the code
|
37
53
|
def request_email_code(password)
|
38
54
|
response = default_connection.post(@gen_certificate_path, payload(password))
|
39
55
|
|
@@ -43,6 +59,10 @@ module NubankSdk
|
|
43
59
|
response_parsed[:sent_to]
|
44
60
|
end
|
45
61
|
|
62
|
+
#
|
63
|
+
# Verify communication with the nubank api
|
64
|
+
#
|
65
|
+
# @return [File] the certificate file
|
46
66
|
def exchange_certs(email_code, password)
|
47
67
|
response = default_connection.post(@gen_certificate_path, payload(password).merge({
|
48
68
|
code: email_code,
|
@@ -51,11 +71,17 @@ module NubankSdk
|
|
51
71
|
)
|
52
72
|
|
53
73
|
response_data = Client.get_body(response)
|
54
|
-
certificate.process_decoded response_data[:certificate]
|
74
|
+
certificate.process_decoded(key, response_data[:certificate])
|
55
75
|
end
|
56
76
|
|
57
77
|
private
|
58
78
|
|
79
|
+
# @!visibility private
|
80
|
+
# parse the headers of the authenticate response
|
81
|
+
#
|
82
|
+
# @param [String] header_content the headers to parse
|
83
|
+
#
|
84
|
+
# @return [Hash] the parsed header
|
59
85
|
def parse_authenticate_headers(header_content)
|
60
86
|
chunks = header_content.split(',')
|
61
87
|
parsed = {}
|
@@ -70,16 +96,28 @@ module NubankSdk
|
|
70
96
|
parsed
|
71
97
|
end
|
72
98
|
|
99
|
+
# @!visibility private
|
100
|
+
# Create a payload to generate a new certificate
|
101
|
+
#
|
102
|
+
# @param [String] password the password to authenticate
|
103
|
+
#
|
104
|
+
# @return [Hash] the payload to generate a new certificate
|
73
105
|
def payload(password)
|
74
106
|
{
|
75
107
|
login: @cpf,
|
76
108
|
password: password,
|
77
|
-
public_key:
|
109
|
+
public_key: key.public_key.to_pem,
|
78
110
|
device_id: @device_id,
|
79
111
|
model: "NubankSdk Client (#@device_id)",
|
80
112
|
}
|
81
113
|
end
|
82
114
|
|
115
|
+
# @!visibility private
|
116
|
+
# Create a payload to authenticate with the nubank api
|
117
|
+
#
|
118
|
+
# @param [String] password the password to authenticate
|
119
|
+
#
|
120
|
+
# @return [Hash] the payload to authenticate
|
83
121
|
def token_payload(password)
|
84
122
|
{
|
85
123
|
'grant_type': 'password',
|
@@ -90,10 +128,20 @@ module NubankSdk
|
|
90
128
|
}
|
91
129
|
end
|
92
130
|
|
131
|
+
# @!visibility private
|
132
|
+
# Generates a new key for the certificate communication
|
133
|
+
#
|
134
|
+
# @return [OpenSSL::PKey::RSA] a new key
|
93
135
|
def generate_key
|
94
136
|
OpenSSL::PKey::RSA.new 2048
|
95
137
|
end
|
96
138
|
|
139
|
+
# @!visibility private
|
140
|
+
# Add the new links to the api routes
|
141
|
+
#
|
142
|
+
# @param [Hash] links the new links to add
|
143
|
+
#
|
144
|
+
# @return [NubankSdk::ApiRoutes] the api routes with the new links
|
97
145
|
def update_api_routes(links)
|
98
146
|
feed_url_keys = ['events', 'magnitude']
|
99
147
|
bills_url_keys = ['bills_summary']
|
@@ -105,33 +153,69 @@ module NubankSdk
|
|
105
153
|
@api_routes.add_entrypoint(path: :ssl, entrypoint: :bills, url: find_url(bills_url_keys, links))
|
106
154
|
@api_routes.add_entrypoint(path: :ssl, entrypoint: :customer, url: find_url(customer_url_keys, links))
|
107
155
|
@api_routes.add_entrypoint(path: :ssl, entrypoint: :account, url: find_url(account_url_keys, links))
|
156
|
+
@api_routes
|
108
157
|
end
|
109
158
|
|
159
|
+
# @!visibility private
|
160
|
+
# Return the url of the first key found in the links
|
161
|
+
#
|
162
|
+
# @param [Array] keys the keys to search in the links
|
163
|
+
# @param [Hash] list of the links to search in
|
164
|
+
#
|
165
|
+
# @return [String] the url of the first key found
|
110
166
|
def find_url(keys, list)
|
111
167
|
links_keys = list.keys
|
112
168
|
|
113
|
-
keys.each do |
|
114
|
-
return list[
|
169
|
+
keys.each do |url_key|
|
170
|
+
return list[url_key]['href'] if links_keys.include?(url_key)
|
115
171
|
end
|
116
172
|
''
|
117
173
|
end
|
118
174
|
|
119
|
-
|
175
|
+
# @!visibility private
|
176
|
+
# Generates a new connection with certificate
|
177
|
+
#
|
178
|
+
# @return [Client::HTTPS] a new connection with certificate
|
179
|
+
def prepare_default_connection
|
120
180
|
uri, @gen_certificate_path = @api_routes.entrypoint(
|
121
181
|
path: :app,
|
122
182
|
entrypoint: :gen_certificate,
|
123
183
|
type: :splitted
|
124
184
|
)
|
125
185
|
|
126
|
-
Client::HTTP.new(uri, @
|
186
|
+
Client::HTTP.new(uri, @connection_adapter)
|
127
187
|
end
|
128
188
|
|
189
|
+
# @!visibility private
|
190
|
+
# Create a new default connection to the nubank api
|
191
|
+
#
|
192
|
+
# @return [Client::HTTP] a new default connection
|
129
193
|
def default_connection
|
130
|
-
@default_connection ||=
|
194
|
+
@default_connection ||= prepare_default_connection
|
131
195
|
end
|
132
196
|
|
197
|
+
# @!visibility private
|
198
|
+
# Create a new ssl connection to the nubank api
|
199
|
+
#
|
200
|
+
# @return [Client::HTTPS] a new ssl connection
|
133
201
|
def ssl_connection
|
134
|
-
@ssl_connection ||= Client::HTTPS.new(certificate.encoded, @
|
202
|
+
@ssl_connection ||= Client::HTTPS.new(certificate.encoded, @connection_adapter)
|
203
|
+
end
|
204
|
+
|
205
|
+
# @!visibility private
|
206
|
+
# return the key of the certificate communication
|
207
|
+
#
|
208
|
+
# @return [OpenSSL::PKey::RSA] the key of the certificate
|
209
|
+
def key
|
210
|
+
@key ||= generate_key
|
211
|
+
end
|
212
|
+
|
213
|
+
# @!visibility private
|
214
|
+
# Generates a random device id
|
215
|
+
#
|
216
|
+
# @return [String] a random device id
|
217
|
+
def generate_device_id
|
218
|
+
SecureRandom.uuid.split('-').last
|
135
219
|
end
|
136
220
|
end
|
137
221
|
end
|
@@ -1,43 +1,78 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
require 'openssl'
|
4
2
|
|
5
3
|
module NubankSdk
|
6
4
|
class Certificate
|
7
|
-
FILES_PATH = './certificates/'
|
5
|
+
FILES_PATH = './certificates/'.freeze
|
8
6
|
|
9
|
-
|
7
|
+
#
|
8
|
+
# Controller of certifications
|
9
|
+
#
|
10
|
+
# @param [String] cpf
|
11
|
+
def initialize(cpf)
|
10
12
|
@cpf = cpf
|
11
|
-
@key = key
|
12
13
|
end
|
13
14
|
|
14
|
-
|
15
|
+
#
|
16
|
+
# Create a certificate file
|
17
|
+
#
|
18
|
+
# @param [OpenSSL::PKey::RSA] key
|
19
|
+
# @param [String] certificate
|
20
|
+
#
|
21
|
+
# @return [File]
|
22
|
+
def process_decoded(key, certificate)
|
15
23
|
encoded = encode certificate
|
16
24
|
|
17
|
-
p12 = create_pkcs12_from encoded
|
25
|
+
p12 = create_pkcs12_from(key, encoded)
|
18
26
|
save p12
|
19
27
|
end
|
20
28
|
|
29
|
+
#
|
30
|
+
# Load the certificate file
|
31
|
+
#
|
32
|
+
# @return [OpenSSL::PKCS12]
|
21
33
|
def encoded
|
22
34
|
@encoded ||= OpenSSL::PKCS12.new(file.read, 'password')
|
23
35
|
end
|
24
36
|
|
25
37
|
private
|
26
38
|
|
39
|
+
# @!visibility private
|
40
|
+
# Open the encrypted certificate file
|
41
|
+
#
|
42
|
+
# @return [OpenSSL::PKCS12]
|
27
43
|
def file
|
28
44
|
File.open("#{FILES_PATH}#{@cpf}.p12", 'rb')
|
29
45
|
end
|
30
46
|
|
47
|
+
# @!visibility private
|
48
|
+
# Create a file with the encrypted certificate
|
49
|
+
#
|
50
|
+
# @param [OpenSSL::PKCS12] p12
|
51
|
+
#
|
52
|
+
# @return [File]
|
31
53
|
def save(p12)
|
32
54
|
File.open("#{FILES_PATH}#{@cpf}.p12", 'wb') do |file|
|
33
55
|
file.write p12.to_der
|
34
56
|
end
|
35
57
|
end
|
36
58
|
|
37
|
-
|
38
|
-
|
59
|
+
# @!visibility private
|
60
|
+
# crypt key and certificate to pkcs12
|
61
|
+
#
|
62
|
+
# @param [OpenSSL::PKey::RSA] key
|
63
|
+
# @param [OpenSSL::X509::Certificate] certificate
|
64
|
+
#
|
65
|
+
# @return [OpenSSL::PKCS12]
|
66
|
+
def create_pkcs12_from(key, certificate)
|
67
|
+
OpenSSL::PKCS12.create('password', 'key', key, certificate)
|
39
68
|
end
|
40
69
|
|
70
|
+
# @!visibility private
|
71
|
+
# Enconde certificate string to certificate object
|
72
|
+
#
|
73
|
+
# @param [String] certificate
|
74
|
+
#
|
75
|
+
# @return [OpenSSL::X509::Certificate]
|
41
76
|
def encode(certificate)
|
42
77
|
OpenSSL::X509::Certificate.new certificate
|
43
78
|
end
|
data/lib/nubank_sdk/client.rb
CHANGED
@@ -1,22 +1,38 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
require 'faraday'
|
4
2
|
require 'json'
|
5
3
|
|
6
4
|
module NubankSdk
|
7
5
|
module Client
|
6
|
+
#
|
7
|
+
# Parse the response body symbolizing keys
|
8
|
+
#
|
9
|
+
# @param [Faraday::Response] response
|
10
|
+
#
|
11
|
+
# @return [Hash]
|
8
12
|
def self.get_body(response)
|
9
13
|
JSON.parse(response.body, symbolize_names: true)
|
10
14
|
end
|
11
15
|
|
12
16
|
class HTTP
|
13
|
-
|
17
|
+
#
|
18
|
+
# create a new connection with the given url in Faraday
|
19
|
+
#
|
20
|
+
# @param [String] base_url
|
21
|
+
# @param [[Symbol, Faraday::Adapter::Test::Stubs]] connection_adapter
|
22
|
+
def initialize(base_url, connection_adapter = nil)
|
14
23
|
@connection = Faraday.new(url: base_url) do |faraday|
|
15
|
-
faraday.adapter
|
16
|
-
faraday.adapter Faraday.default_adapter unless
|
24
|
+
faraday.adapter(*connection_adapter) if connection_adapter
|
25
|
+
faraday.adapter Faraday.default_adapter unless connection_adapter
|
17
26
|
end
|
18
27
|
end
|
19
28
|
|
29
|
+
#
|
30
|
+
# make put on connection with the given path
|
31
|
+
#
|
32
|
+
# @param [String] path
|
33
|
+
# @param [Hash] body
|
34
|
+
#
|
35
|
+
# @return [Faraday::Response]
|
20
36
|
def post(path, body)
|
21
37
|
@connection.post(path) do |req|
|
22
38
|
req.headers['Content-Type'] = 'application/json'
|
@@ -24,30 +40,53 @@ module NubankSdk
|
|
24
40
|
end
|
25
41
|
end
|
26
42
|
|
43
|
+
#
|
44
|
+
# make get on connection with the given path
|
45
|
+
#
|
46
|
+
# @param [String] path
|
47
|
+
#
|
48
|
+
# @return [Faraday::Response]
|
27
49
|
def get(path)
|
28
50
|
@connection.get(path)
|
29
51
|
end
|
30
52
|
end
|
31
53
|
|
32
54
|
class HTTPS
|
33
|
-
|
55
|
+
attr_accessor :headers
|
56
|
+
|
57
|
+
#
|
58
|
+
# Create a new instance of Faraday::Connection with client certificate
|
59
|
+
#
|
60
|
+
# @param [OpenSSL::PKCS12] certificate
|
61
|
+
# @param [[Symbol, Faraday::Adapter::Test::Stubs]] connection_adapter
|
62
|
+
def initialize(certificate, connection_adapter = nil)
|
34
63
|
client_cert = OpenSSL::X509::Certificate.new(certificate.certificate)
|
35
64
|
client_key = OpenSSL::PKey::RSA.new(certificate.key)
|
36
65
|
|
37
|
-
@connection = Faraday.new(
|
38
|
-
|
39
|
-
|
40
|
-
|
66
|
+
@connection = Faraday.new(
|
67
|
+
ssl: {
|
68
|
+
client_cert: client_cert,
|
69
|
+
client_key: client_key
|
70
|
+
}
|
71
|
+
) { |faraday| faraday.adapter(*connection_adapter) if connection_adapter }
|
72
|
+
@headers = {}
|
41
73
|
end
|
42
74
|
|
43
|
-
|
75
|
+
#
|
76
|
+
# Make a post request on connection
|
77
|
+
#
|
78
|
+
# @param [String] url
|
79
|
+
# @param [Hash] body
|
80
|
+
#
|
81
|
+
# @return [Faraday::Response]
|
82
|
+
def post(url, body)
|
44
83
|
@connection.post(url) do |req|
|
45
84
|
req.headers['Content-Type'] = 'application/json'
|
46
85
|
req.headers['X-Correlation-Id'] = '772428d8-f0ee-43d6-8093-a13de3c9ce96'
|
47
86
|
req.headers['User-Agent'] = "NubankSdk Client (#{NubankSdk::VERSION})"
|
48
87
|
|
49
|
-
headers.each do |
|
50
|
-
req.headers[
|
88
|
+
@headers.each do |header_key, value|
|
89
|
+
req.headers[header_key] = value
|
51
90
|
end
|
52
91
|
|
53
92
|
req.body = body.to_json
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module NubankSdk
|
2
|
+
class User
|
3
|
+
#
|
4
|
+
# Controller of user actions in nubank
|
5
|
+
#
|
6
|
+
# @param [String] cpf
|
7
|
+
# @param [[Symbol, Faraday::Adapter::Test::Stubs]] connection_adapter
|
8
|
+
def initialize(cpf:, connection_adapter: nil)
|
9
|
+
@cpf = cpf
|
10
|
+
@connection_adapter = connection_adapter
|
11
|
+
end
|
12
|
+
|
13
|
+
#
|
14
|
+
# Returns instance of authentications methods
|
15
|
+
#
|
16
|
+
# @return [NubankSdk::Auth]
|
17
|
+
def auth
|
18
|
+
@auth ||= NubankSdk::Auth.new(
|
19
|
+
cpf: @cpf,
|
20
|
+
api_routes: api_routes,
|
21
|
+
connection_adapter: @connection_adapter
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# Returns instance of account methods
|
27
|
+
#
|
28
|
+
# @return [NubankSdk::Account]
|
29
|
+
def account
|
30
|
+
@account ||= NubankSdk::Account.new(connection: connection, api_routes: api_routes)
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# An instance of apis routes
|
35
|
+
#
|
36
|
+
# @return [NubankSdk::ApiRoutes]
|
37
|
+
def api_routes
|
38
|
+
@api_routes ||= NubankSdk::ApiRoutes.new
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
# @!visibility private
|
44
|
+
# Returns connection with client https certificate and authorized
|
45
|
+
#
|
46
|
+
# @return [Faraday::Connection]
|
47
|
+
def connection
|
48
|
+
@connection ||= setup_connection
|
49
|
+
end
|
50
|
+
|
51
|
+
# @!visibility private
|
52
|
+
# Setup connection with client https certificate and authorized
|
53
|
+
#
|
54
|
+
# @return [Faraday::Connection]
|
55
|
+
def setup_connection
|
56
|
+
connection = Client::HTTPS.new(
|
57
|
+
auth.certificate.encoded,
|
58
|
+
@connection_adapter
|
59
|
+
)
|
60
|
+
connection.headers = { 'Authorization': "Bearer #{auth.access_token}" }
|
61
|
+
connection
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/nubank_sdk/version.rb
CHANGED
data/nubank_sdk.gemspec
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
lib = File.expand_path("../lib", __FILE__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
3
|
require "nubank_sdk/version"
|
@@ -25,7 +24,11 @@ Gem::Specification.new do |spec|
|
|
25
24
|
spec.add_development_dependency "bundler", "~> 1.17"
|
26
25
|
spec.add_development_dependency "rake", "~> 10.0"
|
27
26
|
spec.add_development_dependency "rspec", "~> 3.0"
|
27
|
+
spec.add_development_dependency "rubocop", "~> 0.57.2"
|
28
|
+
spec.add_development_dependency "rubocop-rspec", "~> 1.27.0"
|
29
|
+
spec.add_development_dependency "factory_bot", "~> 4.8.2"
|
30
|
+
spec.add_development_dependency "yard", "~> 0.9.12"
|
28
31
|
|
29
32
|
spec.add_dependency "faraday", "~> 0.15.0"
|
30
|
-
spec.add_dependency "json", "~> 2.
|
33
|
+
spec.add_dependency "json", "~> 2.3"
|
31
34
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nubank_sdk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Viserion77
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-11-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,6 +52,62 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubocop
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.57.2
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.57.2
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop-rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 1.27.0
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.27.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: factory_bot
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 4.8.2
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 4.8.2
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: yard
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 0.9.12
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 0.9.12
|
55
111
|
- !ruby/object:Gem::Dependency
|
56
112
|
name: faraday
|
57
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -72,14 +128,14 @@ dependencies:
|
|
72
128
|
requirements:
|
73
129
|
- - "~>"
|
74
130
|
- !ruby/object:Gem::Version
|
75
|
-
version: 2.
|
131
|
+
version: '2.3'
|
76
132
|
type: :runtime
|
77
133
|
prerelease: false
|
78
134
|
version_requirements: !ruby/object:Gem::Requirement
|
79
135
|
requirements:
|
80
136
|
- - "~>"
|
81
137
|
- !ruby/object:Gem::Version
|
82
|
-
version: 2.
|
138
|
+
version: '2.3'
|
83
139
|
description: Monitorize balances, recent transactions, credit limit etc...
|
84
140
|
email:
|
85
141
|
- jeferson.a.oficial@gmail.com
|
@@ -87,8 +143,10 @@ executables: []
|
|
87
143
|
extensions: []
|
88
144
|
extra_rdoc_files: []
|
89
145
|
files:
|
146
|
+
- ".github/workflows/gem-push.yml"
|
90
147
|
- ".gitignore"
|
91
148
|
- ".rspec"
|
149
|
+
- ".rubocop.yml"
|
92
150
|
- ".travis.yml"
|
93
151
|
- ".vscode/settings.json"
|
94
152
|
- Gemfile
|
@@ -105,9 +163,9 @@ files:
|
|
105
163
|
- lib/nubank_sdk/auth.rb
|
106
164
|
- lib/nubank_sdk/certificate.rb
|
107
165
|
- lib/nubank_sdk/client.rb
|
166
|
+
- lib/nubank_sdk/user.rb
|
108
167
|
- lib/nubank_sdk/version.rb
|
109
168
|
- nubank_sdk.gemspec
|
110
|
-
- usage_example.rb
|
111
169
|
homepage: https://github.com/Viserion77/nubank_sdk
|
112
170
|
licenses: []
|
113
171
|
metadata: {}
|
@@ -126,7 +184,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
126
184
|
- !ruby/object:Gem::Version
|
127
185
|
version: '0'
|
128
186
|
requirements: []
|
129
|
-
rubygems_version: 3.0.
|
187
|
+
rubygems_version: 3.0.3.1
|
130
188
|
signing_key:
|
131
189
|
specification_version: 4
|
132
190
|
summary: A gem to make it ease to monitorize your Nubank account.
|
data/usage_example.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
require 'nubank_sdk'
|
2
|
-
|
3
|
-
# instance a nubank account object
|
4
|
-
account = NubankSdk::Account.new(cpf: '12345678909')
|
5
|
-
password = 'dracarys'
|
6
|
-
# authenticate the account
|
7
|
-
|
8
|
-
# request an email code
|
9
|
-
account_email = account.auth.request_email_code(password)
|
10
|
-
|
11
|
-
# get the email code from the user
|
12
|
-
puts "Enter the code sent to #{account_email}: "
|
13
|
-
email_code = gets.chomp
|
14
|
-
account.auth.exchange_certs(email_code, password)
|
15
|
-
|
16
|
-
account.auth.authenticate_with_certificate(password)
|
17
|
-
|
18
|
-
# get the account balance
|
19
|
-
account.account_balance
|