nubank_sdk 0.4.1 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
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