banker 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ kyle.rb
6
+ chuck.rb
7
+ coverage
8
+ *.un~
9
+ *.log
data/.rvmrc ADDED
@@ -0,0 +1,2 @@
1
+ rvm use ruby-1.9.2@banker --create
2
+ rvm use ruby-1.9.3@banker --create
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ script: rspec
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in banker.gemspec
4
+ gemspec
5
+
6
+ gem 'guard-rspec'
7
+ gem "cane", "~> 1.1.0"
data/Guardfile ADDED
@@ -0,0 +1,10 @@
1
+ guard 'rspec', :notification => true, :version => 2, :cli => "--colour", :run_all => {:cli => '--profile'} do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec/" }
5
+ end
6
+
7
+ guard 'bundler' do
8
+ watch('Gemfile')
9
+ watch(/^.+\.gemspec/)
10
+ end
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2012 BritRuby LTD
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.
23
+
data/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # Banker [![CI Build Status](https://secure.travis-ci.org/BritRuby/Banker.png?branch=master)][travis] [![Dependency Status](https://gemnasium.com/BritRuby/Banker.png?travis)][gemnasium]
2
+
3
+ [travis]:http://travis-ci.org/BritRuby/Banker
4
+ [gemnasium]:https://gemnasium.com/BritRuby/Banker
5
+
6
+ A collection of strategies to access online bank accounts to obtain balance
7
+ and transaction details.
8
+
9
+ ## Supported Institutes
10
+
11
+ * Barclay's Bank => Barclays
12
+ * Barclaycard UK => BarclaycardUK
13
+ * Capital One UK => CapitalOneUK
14
+ * Lloyds TSB UK => LloydsTSBUK
15
+
16
+ ####Extras
17
+
18
+ * Credit Expert UK => CreidtExpertUK
19
+
20
+ ## Installation
21
+
22
+ Add this line to your application's Gemfile:
23
+
24
+ gem 'squeegee'
25
+
26
+ And then execute:
27
+
28
+ $ bundle
29
+
30
+ Or install it yourself as:
31
+
32
+ $ gem install squeegee
33
+
34
+ ## Example Usage
35
+
36
+
37
+
38
+ user_params = {
39
+ surname: "Bloggs",
40
+ username: "Bloggs123",
41
+ password: "password",
42
+ memorable_word: "superduper",
43
+ card_number: 4111111111111111,
44
+ date_of_birth: Date.parse('2012-01-01')
45
+ }
46
+
47
+ Get the balance in pennies of the account.
48
+
49
+ # Barclays Bank
50
+ Banker::Barclays.new(user_params).accounts.first
51
+ # => <Banker::Account @name="Barclays Bank", @uid="cdd5f8e1c6e441fd9aac2786ca38c835", @amount=130000, @limit=-150000, @currency="GBP">
52
+
53
+
54
+ Extra strategies
55
+
56
+ # Credit Expert UK
57
+ Banker::CreditExpertUK.new(user_params).score #=> 800
58
+
59
+
60
+ ## Dependancies
61
+
62
+ * Mechanize
63
+ * OFX
64
+
65
+ ## Alternate Languages
66
+
67
+ * Python [Bank Scraper](https://github.com/MoneyToolkit/Bank-Scraper) from MoneyToolKit
68
+
69
+ ## Contributing
70
+
71
+ 1. Fork it
72
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
73
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
74
+ 4. Push to the branch (`git push origin my-new-feature`)
75
+ 5. Create new Pull Request
76
+
77
+ ## License
78
+
79
+ This library is distributed under the MIT license. Please see the [LICENSE](https://github.com/BritRuby/Banker/LICENSE.md) file.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
data/banker.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/banker/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ['Kyle Welsby', 'Chuck Hardy']
6
+ gem.email = ['app@britishruby.com']
7
+ gem.homepage = 'https://github.com/BritRuby/Banker'
8
+ gem.description = %q{A collection of strategies to access online bank accounts to obtain balance and transaction details.}
9
+ gem.summary = gem.description
10
+
11
+ gem.add_runtime_dependency 'mechanize'
12
+ gem.add_runtime_dependency 'ofx'
13
+
14
+ gem.add_development_dependency "gem-release"
15
+ gem.add_development_dependency 'rspec'
16
+ gem.add_development_dependency 'webmock'
17
+ gem.add_development_dependency 'simplecov'
18
+
19
+ gem.files = `git ls-files`.split("\n")
20
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
22
+ gem.name = "banker"
23
+ gem.require_paths = ['lib']
24
+ gem.version = Banker::VERSION
25
+ end
data/lib/banker.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'ofx'
2
+ require 'csv'
3
+ require 'digest/md5'
4
+ require 'mechanize'
5
+ require 'logger'
6
+ require 'banker/version'
7
+ require 'banker/base'
8
+ require 'banker/error'
9
+ require 'banker/account'
10
+ require 'banker/barclays'
11
+ require 'banker/lloyds_tsb_uk'
12
+ require 'banker/capital_one_uk'
13
+ require 'banker/barclaycard_uk'
14
+ require 'banker/credit_expert_uk'
@@ -0,0 +1,16 @@
1
+ module Banker
2
+ class Account
3
+ attr_accessor :name, :uid, :amount, :limit, :currency
4
+ def initialize(args = {})
5
+ raise Banker::Error::InvalidParams, "missing attribute `name`" unless args.has_key?(:name)
6
+ raise Banker::Error::InvalidParams, "missing attribute `uid`" unless args.has_key?(:uid)
7
+ raise Banker::Error::InvalidParams, "missing attribute `amount`" unless args.has_key?(:amount)
8
+
9
+ @name = args[:name]
10
+ @uid = args[:uid]
11
+ @amount = args[:amount]
12
+ @limit = args[:limit]
13
+ @currency = args[:currency]
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,96 @@
1
+ module Banker
2
+ # This class allows the retrieval of account data
3
+ # for Barcalycard UK
4
+ #
5
+ # == Examples
6
+ #
7
+ # Get OFX from Barcalycard UK
8
+ #
9
+ # bank = Banker::BarcalycardUK.new(:username => "joe"
10
+ # :password => '123456',
11
+ # :memorable_word => 'superduper')
12
+ #
13
+ # bank.accounts.first.balance #=> 410010
14
+ #
15
+ class BarclaycardUK < Base
16
+ attr_accessor :username, :password, :memorable_word, :accounts, :page
17
+
18
+ LOGIN_ENDPOINT = 'https://bcol.barclaycard.co.uk/ecom/as2/initialLogon.do'
19
+ EXPORT_ENDPOINT = 'https://bcol.barclaycard.co.uk/ecom/as2/export.do?doAction=processRecentExportTransaction&type=OFX_2_0_2&statementDate=&sortBy=transactionDate&sortType=Dsc'
20
+ FIELDS = {
21
+ username: 'username',
22
+ password: 'password',
23
+ memorable_word: [
24
+ 'firstAnswer',
25
+ 'secondAnswer'
26
+ ]
27
+ }
28
+
29
+ def initialize(args = {})
30
+ @keys = [:username, :password, :memorable_word]
31
+ params(args)
32
+ @username = args[:username]
33
+ @password = args[:password]
34
+ @memorable_word = args[:memorable_word]
35
+
36
+ @accounts = []
37
+
38
+ authenticate!
39
+
40
+ get_data
41
+ end
42
+ private
43
+
44
+ def authenticate!
45
+ page = get(LOGIN_ENDPOINT)
46
+ form = page.form_with(:action => '/ecom/as2/initialLogon.do')
47
+
48
+ form[FIELDS[:username]] = @username
49
+ form[FIELDS[:password]] = @password
50
+
51
+ @page = @agent.submit(form, form.buttons.last)
52
+ step2(@page)
53
+ end
54
+
55
+ def step2(page)
56
+
57
+ form = page.form_with(:action => '/ecom/as2/validateMemorableWord.do')
58
+
59
+ first_letter = page.at("label[for='lettera']").content.
60
+ scan(/\d+/).first.to_i
61
+ second_letter = page.at("label[for='letterb']").content.
62
+ scan(/\d+/).first.to_i
63
+
64
+ form[FIELDS[:memorable_word][0]] = get_letter(@memorable_word,
65
+ first_letter).upcase
66
+ form[FIELDS[:memorable_word][1]] = get_letter(@memorable_word,
67
+ second_letter).upcase
68
+
69
+ @page = @agent.submit(form, form.buttons.first)
70
+ @limit = -@page.at(".panelSummary .limit .figure").content.
71
+ gsub(/\D/,'').to_i
72
+ end
73
+
74
+ def get_data
75
+ ofx = get(EXPORT_ENDPOINT)
76
+ ofx = ofx.body.gsub('VERSION="202"','VERSION="200"')
77
+
78
+ @ofx = OFX(ofx)
79
+
80
+ resource = StringIO.new(ofx)
81
+ html = Nokogiri::HTML.parse(
82
+ Iconv.conv("UTF-8", "LATIN1//IGNORE",resource.read)
83
+ )
84
+ account_id = html.search("ccacctfrom > acctid").inner_text
85
+ currency = html.search("ccstmtrs > curdef").inner_text
86
+
87
+ uid = Digest::MD5.hexdigest("Barclayard#{@username}#{account_id}")
88
+ @accounts << Banker::Account.new(uid: uid,
89
+ name: "Barclaycard",
90
+ amount: @ofx.account.balance.amount_in_pennies,
91
+ currency: currency,
92
+ limit: @limit
93
+ )
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,89 @@
1
+ module Banker
2
+ class Barclays < Base
3
+
4
+ LOGIN_URL = "https://bank.barclays.co.uk/olb/auth/LoginLink.action"
5
+ EXPORT_URL = "https://bank.barclays.co.uk/olb/balances/ExportDataStep1.action"
6
+
7
+ attr_accessor :accounts, :ofx
8
+
9
+ FIELD = {
10
+ surname: "surname",
11
+ membership_number: "membershipNumber",
12
+ passcode: 'passcode',
13
+ memorable_word: [
14
+ 'firstMemorableCharacter',
15
+ 'secondMemorableCharacter'
16
+ ]
17
+ }
18
+
19
+ def initialize(args={})
20
+ @keys = %w(surname membership_number passcode memorable_word)
21
+
22
+ params(args)
23
+ @surname = args.delete(:surname)
24
+ @membership_number = args.delete(:membership_number)
25
+ @passcode = args.delete(:passcode)
26
+ @memorable_word = args.delete(:memorable_word)
27
+
28
+ @accounts = []
29
+
30
+ authenticate!
31
+ delivery(download!)
32
+ end
33
+
34
+ private
35
+
36
+ def authenticate!
37
+ page = get(LOGIN_URL)
38
+ form = page.form_with(action: 'LoginLink.action')
39
+
40
+ form[FIELD[:surname]] = @surname
41
+ form[FIELD[:membership_number]] = @membership_number
42
+
43
+ page = @agent.submit(form, form.buttons.first)
44
+
45
+ form = page.form_with(action: 'LoginStep1i.action')
46
+ form.radiobuttons.first.check
47
+ page = @agent.submit(form, form.buttons.first)
48
+
49
+ form = page.form_with(action: 'LoginStep2.action')
50
+ letters = memorable_required(page).delete_if { |v| v if v == 0 }
51
+
52
+ form[FIELD[:passcode]] = @passcode
53
+ form[FIELD[:memorable_word][0]] = get_letter(@memorable_word, letters[0])
54
+ form[FIELD[:memorable_word][1]] = get_letter(@memorable_word, letters[1])
55
+
56
+ @agent.submit(form, form.buttons.first)
57
+ end
58
+
59
+ def memorable_required(page)
60
+ page.labels.collect { |char| cleaner(char.to_s).to_i }
61
+ end
62
+
63
+ def cleaner(str)
64
+ str.gsub(/[^\d+]/, '')
65
+ end
66
+
67
+ def download!
68
+ page = get(EXPORT_URL)
69
+ form = page.form_with(action: "/olb/balances/ExportDataStep1.action")
70
+ form['reqSoftwarePkgCode'] = '6'
71
+ form['productIdentifier'] = 'All'
72
+ page = @agent.submit(form, form.buttons.first)
73
+ form = page.form_with(:action => "/olb/balances/ExportDataStep2All.action")
74
+ file = @agent.submit(form, form.buttons.last)
75
+ return OFX(file.body)
76
+ end
77
+
78
+ def delivery(ofx)
79
+ amount = ofx.account.balance.amount_in_pennies
80
+ uid = Digest::MD5.hexdigest("Barclays#{@membership_number}")
81
+
82
+ @accounts << Banker::Account.new(uid: uid,
83
+ name: "Barclays",
84
+ amount: amount
85
+ )
86
+ end
87
+
88
+ end
89
+ end
@@ -0,0 +1,28 @@
1
+ module Banker
2
+ class Base
3
+ attr_writer :keys
4
+ def params(args)
5
+ missing_keys = []
6
+ return unless defined? @keys
7
+ @keys.each do |key|
8
+ missing_keys << key unless args.has_key?(key.to_sym)
9
+ end
10
+ if missing_keys.any?
11
+ raise Error::InvalidParams,
12
+ "missing parameters #{missing_keys.map {|key| "`#{key}` "}.join}"
13
+ end
14
+ end
15
+
16
+ def get(url)
17
+ @agent ||= Mechanize.new
18
+ @agent.log = Logger.new 'banker.log'
19
+ @agent.user_agent = "Mozilla/5.0 (Banker)"
20
+ @agent.force_default_encoding = "utf8"
21
+ @agent.get(url)
22
+ end
23
+
24
+ def get_letter(value,index)
25
+ value.to_s[index-1]
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,88 @@
1
+ module Banker
2
+ # This class allows the data retrieval of account balaces
3
+ # for Capital One UK
4
+ #
5
+ # == Examples
6
+ #
7
+ # Make a new connection
8
+ #
9
+ # bank = Banker::CapitalOneUK.new(:username => "Joe", :password => "password")
10
+ #
11
+ # bank.accounts.first.balance.amount #=> 4100.10
12
+ #
13
+ class CapitalOneUK < Base
14
+ attr_accessor :username, :password, :accounts
15
+ attr_reader :page
16
+
17
+ LOGIN_ENDPOINT = "https://www.capitaloneonline.co.uk/CapitalOne_Consumer/Login.do"
18
+ FIELD = {
19
+ username: 'username',
20
+ password: [
21
+ 'password.randomCharacter0',
22
+ 'password.randomCharacter1',
23
+ 'password.randomCharacter2'
24
+ ]
25
+ }
26
+
27
+ def initialize(args = {})
28
+ @keys = ['username', 'password']
29
+ params(args)
30
+ @username = args[:username]
31
+ @password = args[:password]
32
+
33
+ @accounts = []
34
+
35
+ authenticate!
36
+ get_data
37
+ end
38
+
39
+ private
40
+
41
+ def authenticate!
42
+ page = get(LOGIN_ENDPOINT)
43
+
44
+ form = page.form_with(name: 'logonForm')
45
+
46
+ form[FIELD[:username]] = @username
47
+ letters_html = page.at("#sign_in_box .password").content
48
+ letters = letters_html.scan(/(\d)/).collect { |letter| letter[0].to_i }
49
+
50
+ form[FIELD[:password][0]] = get_letter(@password, letters[0])
51
+ form[FIELD[:password][1]] = get_letter(@password, letters[1])
52
+ form[FIELD[:password][2]] = get_letter(@password, letters[2])
53
+
54
+ @page = @agent.submit(form, form.buttons.first)
55
+ end
56
+
57
+ #def letters
58
+ #html = page.search('#sign_in_box .password').content
59
+ #end
60
+
61
+ def get_data
62
+ limit = -@page.at("table[summary='account summary'] tr:nth-child(1) td.normalText:nth-child(2)").content.gsub(/\D/,'').to_i
63
+ amount = -@page.at("table[summary='account summary'] tr:nth-child(2) td.normalText:nth-child(2)").content.gsub(/\D/,'').to_i
64
+ account_number = @page.at("table:first-child tr:nth-child(5) td b").
65
+ content.to_i
66
+
67
+ uid = Digest::MD5.hexdigest("CapitalOneUK#{account_number}")
68
+
69
+ @accounts << Banker::Account.new(name: "Capital Account",
70
+ amount: amount,
71
+ limit: limit,
72
+ uid: uid,
73
+ currency: "GBP"
74
+ )
75
+
76
+ #form = page.form('DownLoadTransactionForm')
77
+
78
+ #form.downloadType = 'csv'
79
+
80
+ #csv_data = @agent.submit(form, form.buttons.first)
81
+
82
+ #csv = csv_data.body.gsub(/,\s*/, ',')
83
+
84
+ #@transactions = CSV.parse(csv, :headers => true)
85
+ end
86
+
87
+ end
88
+ end