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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ee3ffd6c280bb2b48dba0c1b46d4c3a560f985a55b22793aa251ece82f69da28
4
- data.tar.gz: a4a69ad6502fd3852fb91b21fc37ee2fe872f9432a575d8d72367125201d8ecd
3
+ metadata.gz: 88e935ce5affb735e7676f0dd93a213658308ce205f837028172bac24d0ceb0e
4
+ data.tar.gz: dc9bd5c2cc72d965a2cfa0bb20e81eb3e43e6f4077806cfcab04532c1db00181
5
5
  SHA512:
6
- metadata.gz: 232922b481796a104a241ee8a9c890082c93c4e23eb3de6635f06633539c0d87770cb0e85b86e1a72d94548004080a5277010cb529ab623299ac40364fa07ac9
7
- data.tar.gz: 1e82254c8026f4f5cb4e9bd3d80971d31979aced7145fe15e44e9e7a7a9a4c0f6303e232f3468860b07b5ab5cf358b624f1da4f0e03669765494a4f07fd21cd2
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
@@ -0,0 +1,5 @@
1
+ require:
2
+ - rubocop-rspec
3
+
4
+ Style/BracesAroundHashParameters:
5
+ Enabled: false
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "cSpell.words": [
3
+ "dracarys",
3
4
  "faraday",
4
5
  "nubank",
5
6
  "Nubank",
@@ -11,6 +12,7 @@
11
12
  "auth",
12
13
  "client",
13
14
  "version",
14
- "account"
15
+ "account",
16
+ "github"
15
17
  ]
16
18
  }
data/Gemfile CHANGED
@@ -1,6 +1,6 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
3
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
4
 
5
5
  # Specify your gem's dependencies in nubank_sdk.gemspec
6
6
  gemspec
data/Gemfile.lock CHANGED
@@ -1,18 +1,36 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nubank_sdk (0.4.1)
4
+ nubank_sdk (0.5.1)
5
5
  faraday (~> 0.15.0)
6
- json (~> 2.1.0)
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
- json (2.1.0)
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
+ ![Gem](https://img.shields.io/gem/dt/nubank_sdk?color=%23701516&logo=ruby&logoColor=%23701516&style=for-the-badge)
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
- TODO: Write usage instructions here
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
File without changes
@@ -1,43 +1,31 @@
1
1
  module NubankSdk
2
2
  class Account
3
- attr_reader :cpf, :device_id
4
-
5
- def initialize(cpf:, key:, device_id: nil, adapter: nil)
6
- @cpf = cpf
7
- @device_id = device_id || generate_device_id
8
-
9
- @api_routes = NubankSdk::ApiRoutes.new(connection_adapter: adapter)
10
- @adapter = adapter
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
- def auth
15
- @auth ||= NubankSdk::Auth.new(
16
- cpf: @cpf,
17
- key: @key,
18
- device_id: @device_id,
19
- api_routes: @api_routes,
20
- adapter: @adapter
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
- response = connection.post(query_url, {
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 = "https://prod-s0-webapp-proxy.nubank.com.br"
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
- # types: :splitted, :full
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
- return url
28
- else
29
- url_splitted = url.split('/api')
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
@@ -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
- def initialize(cpf:, device_id:, key: nil, api_routes: nil, adapter: nil)
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
- @adapter = adapter
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, @key)
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: @key.public_key.to_pem,
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 |key|
114
- return list[key]['href'] if links_keys.include?(key)
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
- def prepare_connections
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, @adapter)
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 ||= prepare_connections
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, @adapter)
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
- def initialize(cpf, key)
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
- def process_decoded(certificate)
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
- def create_pkcs12_from(certificate)
38
- OpenSSL::PKCS12.create("password", "key", @key, certificate)
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
@@ -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
- def initialize(base_url, adapter = nil)
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 *adapter if adapter
16
- faraday.adapter Faraday.default_adapter unless adapter
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
- def initialize(certificate, adapter = nil)
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(ssl: { client_cert: client_cert, client_key: client_key}) do |faraday|
38
- faraday.adapter *adapter if adapter
39
- faraday.adapter Faraday.default_adapter unless adapter
40
- end
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
- def post(url, body, headers = {})
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 |key, value|
50
- req.headers[key] = value
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
@@ -1,3 +1,3 @@
1
1
  module NubankSdk
2
- VERSION = "0.4.1"
2
+ VERSION = "0.5.1"
3
3
  end
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.1.0"
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.1
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-10-24 00:00:00.000000000 Z
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.1.0
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.1.0
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.9
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