bankscrap-ing 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c8e73208b62cf62248c917e01f2a72da389b603a
4
+ data.tar.gz: 85c8b2218a81abdd5d7459c98d89d3bb89f47b19
5
+ SHA512:
6
+ metadata.gz: 5e121dbf85496c7af677fc900e00f48dbf0f6f51e1352f646d47d7a194fbf8aa9cb0bdc9ddc0a7b0df79aeca0ad5c433e4c3a9e930147c248b81549e0010e3d4
7
+ data.tar.gz: 71299e92b3f55be6feed04afed43e84b44bc7c26171ce19f290ec5c576a7d8effc3354ab96edf0032bf67a66af4f43e3566dc99ca242e6c58180ae3f1c36fe22
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rubocop.yml ADDED
@@ -0,0 +1,5 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.1
3
+
4
+ Metrics/LineLength:
5
+ Max: 120
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in bankscrap-ing.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Raúl Marcos
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # Bankscrap::ING
2
+
3
+ Bankscrap adapter for the API behind ING's [mobile app](https://play.google.com/store/apps/details?id=www.ingdirect.nativeframe&hl=en).
4
+ This adapter is only valid for personal accounts (the ones that work with that mobile APP), not for company accounts.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'bankscrap-ing'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install bankscrap-ing
21
+
22
+ ## Usage
23
+
24
+ For usage instructions please read [Bankscrap readme](https://github.com/bankscrap/bankscrap#usage).
25
+
26
+
27
+ ## Contributing
28
+
29
+ 1. Fork it ( https://github.com/bankscrap/bankscrap-ing/fork )
30
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
31
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
32
+ 4. Push to the branch (`git push origin my-new-feature`)
33
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'bankscrap/ing/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'bankscrap-ing'
8
+ spec.version = Bankscrap::ING::VERSION
9
+ spec.authors = ['Raúl']
10
+ spec.email = ['raulmarcosl@gmail.coms']
11
+ spec.summary = 'ING adapter for Bankscrap'
12
+ spec.homepage = ''
13
+ spec.license = 'MIT'
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ['lib']
19
+
20
+ spec.add_runtime_dependency 'bankscrap', '~> 1.0'
21
+ spec.add_runtime_dependency 'rmagick', '~> 2.2', '>= 2.2.2'
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.7'
24
+ spec.add_development_dependency 'rake', '~> 10.0'
25
+ spec.add_development_dependency 'byebug', '~> 8.2', '>= 8.2.5'
26
+ spec.add_development_dependency 'rubocop', '~> 0.39.0'
27
+ end
@@ -0,0 +1,246 @@
1
+ require 'bankscrap'
2
+
3
+ require 'json'
4
+ require 'base64'
5
+ require 'rmagick'
6
+ require 'tempfile'
7
+
8
+ module Bankscrap
9
+ module ING
10
+ class Bank < ::Bankscrap::Bank
11
+ BASE_ENDPOINT = 'https://ing.ingdirect.es/'.freeze
12
+ LOGIN_ENDPOINT = BASE_ENDPOINT + 'genoma_login/rest/session'
13
+ POST_AUTH_ENDPOINT = BASE_ENDPOINT + 'genoma_api/login/auth/response'
14
+ CLIENT_ENDPOINT = BASE_ENDPOINT + 'genoma_api/rest/client'
15
+ PRODUCTS_ENDPOINT = BASE_ENDPOINT + 'genoma_api/rest/products'
16
+
17
+ SAMPLE_WIDTH = 30
18
+ SAMPLE_HEIGHT = 30
19
+ SAMPLE_ROOT_PATH = '/numbers'.freeze
20
+
21
+ def initialize(user, password, log: false, debug: false, extra_args:)
22
+ @dni = user
23
+ @password = password.to_s
24
+ @birthday = extra_args.with_indifferent_access['birthday']
25
+ @log = log
26
+ @debug = debug
27
+
28
+ initialize_connection
29
+ bundled_login
30
+
31
+ @investments = fetch_investments
32
+
33
+ super
34
+ end
35
+
36
+ def balances
37
+ log 'get_balances'
38
+ balances = {}
39
+ total_balance = 0
40
+ @accounts.each do |account|
41
+ balances[account.description] = account.balance
42
+ total_balance += account.balance
43
+ end
44
+
45
+ balances['TOTAL'] = total_balance
46
+ balances
47
+ end
48
+
49
+ def fetch_accounts
50
+ log 'fetch_accounts'
51
+ add_headers(
52
+ 'Accept' => '*/*',
53
+ 'Content-Type' => 'application/json; charset=utf-8'
54
+ )
55
+
56
+ JSON.parse(get(PRODUCTS_ENDPOINT)).map do |account|
57
+ build_account(account) if account['iban']
58
+ end.compact
59
+ end
60
+
61
+ def fetch_investments
62
+ log 'fetch_investments'
63
+ add_headers(
64
+ 'Accept' => '*/*',
65
+ 'Content-Type' => 'application/json; charset=utf-8'
66
+ )
67
+
68
+ JSON.parse(get(PRODUCTS_ENDPOINT)).map do |investment|
69
+ build_investment(investment) if investment['investment']
70
+ end.compact
71
+ end
72
+
73
+ def fetch_transactions_for(account, start_date: Date.today - 1.month, end_date: Date.today)
74
+ log "fetch_transactions for #{account.id}"
75
+
76
+ # The API allows any limit to be passed, but we better keep
77
+ # being good API citizens and make a loop with a short limit
78
+ params = {
79
+ fromDate: start_date.strftime('%d/%m/%Y'),
80
+ toDate: end_date.strftime('%d/%m/%Y'),
81
+ limit: 25,
82
+ offset: 0
83
+ }
84
+
85
+ transactions = []
86
+ loop do
87
+ request = get("#{PRODUCTS_ENDPOINT}/#{account.id}/movements", params: params)
88
+ json = JSON.parse(request)
89
+ transactions += (json['elements'] || []).map do |transaction|
90
+ build_transaction(transaction, account)
91
+ end
92
+ params[:offset] += 25
93
+ break if (params[:offset] > json['total']) || json['elements'].blank?
94
+ end
95
+ transactions
96
+ end
97
+
98
+ private
99
+
100
+ def bundled_login
101
+ selected_positions = login
102
+ ticket = pass_pinpad(selected_positions)
103
+ post_auth(ticket)
104
+ end
105
+
106
+ def login
107
+ add_headers(
108
+ 'Accept' => 'application/json, text/javascript, */*; q=0.01',
109
+ 'Content-Type' => 'application/json; charset=utf-8'
110
+ )
111
+
112
+ params = {
113
+ loginDocument: {
114
+ documentType: 0,
115
+ document: @dni.to_s
116
+ },
117
+ birthday: @birthday.to_s,
118
+ companyDocument: nil,
119
+ device: 'desktop'
120
+ }
121
+
122
+ response = JSON.parse(post(LOGIN_ENDPOINT, fields: params.to_json))
123
+ current_pinpad_paths = save_pinpad_numbers(response['pinpad'])
124
+ pinpad_numbers = recognize_pinpad_numbers(current_pinpad_paths)
125
+
126
+ get_correct_positions(pinpad_numbers, response['pinPositions'])
127
+ end
128
+
129
+ def pass_pinpad(positions)
130
+ fields = { pinPositions: positions }
131
+ response = put(LOGIN_ENDPOINT, fields: fields.to_json)
132
+ JSON.parse(response)['ticket']
133
+ end
134
+
135
+ def post_auth(ticket)
136
+ add_headers(
137
+ 'Accept' => 'application/json, text/javascript, */*; q=0.01',
138
+ 'Content-Type' => 'application/x-www-form-urlencoded; charset=UTF-8'
139
+ )
140
+
141
+ params = "ticket=#{ticket}&device=desktop"
142
+ post(POST_AUTH_ENDPOINT, fields: params)
143
+ end
144
+
145
+ def save_pinpad_numbers(pinpad)
146
+ current_pinpad_paths = []
147
+ pinpad.each_with_index do |digit, index|
148
+ tmp = Tempfile.new(["pinpad_number_#{index}", '.png'])
149
+ File.open(tmp.path, 'wb') { |f| f.write(Base64.decode64(digit)) }
150
+ current_pinpad_paths << tmp.path
151
+ end
152
+
153
+ current_pinpad_paths
154
+ end
155
+
156
+ def recognize_pinpad_numbers(current_pinpad_paths)
157
+ real_numbers = []
158
+ current_numbers = Magick::ImageList.new(*current_pinpad_paths)
159
+ 0.upto(9) do |i|
160
+ pixel_diffs = []
161
+ 0.upto(9) do |j|
162
+ sample_number_img = Magick::ImageList.new(sample_number_path(j)).first
163
+ pixel_diffs << images_diff(sample_number_img, current_numbers[i])
164
+ end
165
+ real_numbers << pixel_diffs.each_with_index.min.last
166
+ end
167
+ real_numbers
168
+ end
169
+
170
+ def sample_number_path(number)
171
+ File.join(File.dirname(__FILE__), "#{SAMPLE_ROOT_PATH}/#{number}.png")
172
+ end
173
+
174
+ def images_diff(sample_number_img, current_number_img)
175
+ diff = 0
176
+ sample_pixels = pixels_from_coordinates(sample_number_img, 0, 0)
177
+ current_pixels = pixels_from_coordinates(current_number_img, 0, 0)
178
+ current_pixels.each_with_index do |pixel, index|
179
+ sample_pixel = sample_pixels[index]
180
+ red_diff = (pixel.red - sample_pixel.red).abs
181
+ green_diff = (pixel.green - sample_pixel.green).abs
182
+ blue_diff = (pixel.blue - sample_pixel.blue).abs
183
+
184
+ diff += red_diff + green_diff + blue_diff
185
+ end
186
+ diff
187
+ end
188
+
189
+ def pixels_from_coordinates(img, x, y)
190
+ img.get_pixels(x, y, SAMPLE_WIDTH, SAMPLE_HEIGHT)
191
+ end
192
+
193
+ def get_correct_positions(pinpad_numbers, positions)
194
+ first_digit = @password[positions[0] - 1]
195
+ second_digit = @password[positions[1] - 1]
196
+ third_digit = @password[positions[2] - 1]
197
+
198
+ [
199
+ pinpad_numbers.index(first_digit.to_i),
200
+ pinpad_numbers.index(second_digit.to_i),
201
+ pinpad_numbers.index(third_digit.to_i)
202
+ ]
203
+ end
204
+
205
+ # Build an Account object from API data
206
+ def build_account(data)
207
+ Account.new(
208
+ bank: self,
209
+ id: data['uuid'],
210
+ name: data['name'],
211
+ balance: data['balance'],
212
+ currency: 'EUR',
213
+ available_balance: data['availableBalance'],
214
+ description: (data['alias'] || data['name']),
215
+ iban: data['iban'],
216
+ bic: data['bic']
217
+ )
218
+ end
219
+
220
+ def build_investment(data)
221
+ Investment.new(
222
+ bank: self,
223
+ id: data['uuid'],
224
+ name: data['name'],
225
+ balance: data['balance'],
226
+ currency: 'EUR',
227
+ investment: data['investment']
228
+ )
229
+ end
230
+
231
+ # Build a transaction object from API data
232
+ def build_transaction(data, account)
233
+ amount = Money.new(data['amount'] * 100, data['currency'])
234
+ Transaction.new(
235
+ account: account,
236
+ id: data['uuid'],
237
+ amount: amount,
238
+ currency: data['EUR'],
239
+ effective_date: Date.strptime(data['effectiveDate'], '%d/%m/%Y'),
240
+ description: data['description'],
241
+ balance: data['balance']
242
+ )
243
+ end
244
+ end
245
+ end
246
+ end
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,5 @@
1
+ module Bankscrap
2
+ module ING
3
+ VERSION = '1.0.0'.freeze
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ require_relative 'bankscrap/ing/bank'
2
+ require_relative 'bankscrap/ing/version'
metadata ADDED
@@ -0,0 +1,160 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bankscrap-ing
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Raúl
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-05-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bankscrap
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rmagick
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.2'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 2.2.2
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '2.2'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 2.2.2
47
+ - !ruby/object:Gem::Dependency
48
+ name: bundler
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.7'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '1.7'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rake
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '10.0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '10.0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: byebug
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '8.2'
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: 8.2.5
85
+ type: :development
86
+ prerelease: false
87
+ version_requirements: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - "~>"
90
+ - !ruby/object:Gem::Version
91
+ version: '8.2'
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: 8.2.5
95
+ - !ruby/object:Gem::Dependency
96
+ name: rubocop
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: 0.39.0
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - "~>"
107
+ - !ruby/object:Gem::Version
108
+ version: 0.39.0
109
+ description:
110
+ email:
111
+ - raulmarcosl@gmail.coms
112
+ executables: []
113
+ extensions: []
114
+ extra_rdoc_files: []
115
+ files:
116
+ - ".gitignore"
117
+ - ".rubocop.yml"
118
+ - Gemfile
119
+ - LICENSE.txt
120
+ - README.md
121
+ - Rakefile
122
+ - bankscrap-ing.gemspec
123
+ - lib/bankscrap-ing.rb
124
+ - lib/bankscrap/ing/bank.rb
125
+ - lib/bankscrap/ing/numbers/0.png
126
+ - lib/bankscrap/ing/numbers/1.png
127
+ - lib/bankscrap/ing/numbers/2.png
128
+ - lib/bankscrap/ing/numbers/3.png
129
+ - lib/bankscrap/ing/numbers/4.png
130
+ - lib/bankscrap/ing/numbers/5.png
131
+ - lib/bankscrap/ing/numbers/6.png
132
+ - lib/bankscrap/ing/numbers/7.png
133
+ - lib/bankscrap/ing/numbers/8.png
134
+ - lib/bankscrap/ing/numbers/9.png
135
+ - lib/bankscrap/ing/version.rb
136
+ homepage: ''
137
+ licenses:
138
+ - MIT
139
+ metadata: {}
140
+ post_install_message:
141
+ rdoc_options: []
142
+ require_paths:
143
+ - lib
144
+ required_ruby_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ required_rubygems_version: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ requirements: []
155
+ rubyforge_project:
156
+ rubygems_version: 2.2.2
157
+ signing_key:
158
+ specification_version: 4
159
+ summary: ING adapter for Bankscrap
160
+ test_files: []