nordea-rb 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +19 -0
- data/README.markdown +163 -0
- data/Rakefile +25 -0
- data/VERSION +1 -0
- data/bin/nordea +66 -0
- data/lib/nordea/request.rb +42 -0
- data/lib/nordea/resources/account.rb +102 -0
- data/lib/nordea/resources/card.rb +5 -0
- data/lib/nordea/resources/fund.rb +5 -0
- data/lib/nordea/resources/loan.rb +5 -0
- data/lib/nordea/resources/resource.rb +7 -0
- data/lib/nordea/resources.rb +17 -0
- data/lib/nordea/session.rb +63 -0
- data/lib/nordea/support.rb +63 -0
- data/lib/nordea/transaction.rb +27 -0
- data/lib/nordea/version.rb +9 -0
- data/lib/nordea.rb +72 -0
- data/test/fixtures/account.wml +408 -0
- data/test/fixtures/account_10_transactions.wml +257 -0
- data/test/fixtures/account_15_transactions.wml +335 -0
- data/test/fixtures/account_5_transactions.wml +186 -0
- data/test/fixtures/accounts.wml +197 -0
- data/test/fixtures/accounts_overview.wml +111 -0
- data/test/fixtures/error.wml +32 -0
- data/test/fixtures/logged_in_main_menu.wml +133 -0
- data/test/fixtures/login.wml +44 -0
- data/test/fixtures/transfer_to_own_account.wml +175 -0
- data/test/fixtures/transfers.wml +74 -0
- data/test/fixtures/transfers_1.wml +116 -0
- data/test/fixtures/transfers_2.wml +115 -0
- data/test/fixtures/transfers_3.wml +121 -0
- data/test/test_account.rb +168 -0
- data/test/test_functional.rb +63 -0
- data/test/test_helper.rb +58 -0
- data/test/test_nordea.rb +49 -0
- data/test/test_request.rb +44 -0
- data/test/test_session.rb +129 -0
- data/test/test_transaction.rb +58 -0
- metadata +99 -0
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2009 Martin Str�m
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
# Nordea-rb
|
2
|
+
|
3
|
+
|
4
|
+
# First, Please note
|
5
|
+
I don't take no responsable at all if somethings messes up with your bank account or if you break
|
6
|
+
any terms of service (which this lib probably does). It has worked fine for me for a while though.
|
7
|
+
|
8
|
+
|
9
|
+
## What?
|
10
|
+
|
11
|
+
nordea-rb is a small Ruby library to access your Nordea bank account and list balances etc.
|
12
|
+
|
13
|
+
It uses the WAP-version (https://mobil.nordea.se) to access the data. This version has many variables names etc
|
14
|
+
in Swedish so I suppose this only works for the Swedish version right now but it might be easy to adjust it if
|
15
|
+
other languages use the same system.
|
16
|
+
|
17
|
+
I've only tested it with my own login details so there might be issues if you have a different setup.
|
18
|
+
|
19
|
+
|
20
|
+
## Storing the account details in Mac OS X's Keychain
|
21
|
+
|
22
|
+
On Mac OS X, the recommended way of storing your accounts details is in the built-in Keychain.
|
23
|
+
Here's how to set it up:
|
24
|
+
|
25
|
+
1. Run the application "Keychain Access" (located in /Applications/Utilities)
|
26
|
+
2. Add a new Password Item (File → New Password Item)
|
27
|
+
3. You can use which name you'd like for "Keychain Item Name" but by default, nordea-rb will look for an item named "Nordea" (with capital "N")
|
28
|
+
4. Enter your "personnummer" (Swedish SSN) as "Account Name"
|
29
|
+
5. Enter your PIN code as "Password". (yes it indicates that its password strength is "weak")
|
30
|
+
6. Save
|
31
|
+
|
32
|
+
|
33
|
+
## Examples
|
34
|
+
|
35
|
+
Creating a new instance
|
36
|
+
|
37
|
+
# use the following account details
|
38
|
+
n = Nordea.new("1234567890", "1337")
|
39
|
+
|
40
|
+
# or using a hash
|
41
|
+
n = Nordea.new(:pnr => "1234567890", :pin => "1337")
|
42
|
+
|
43
|
+
|
44
|
+
Getting account details from the Keychain:
|
45
|
+
|
46
|
+
# use the default keychain and look for an account called "Nordea"
|
47
|
+
n = Nordea.new(:keychain)
|
48
|
+
|
49
|
+
# default keychain with an account named "My Nordea"
|
50
|
+
n = Nordea.new(:keychain, :label => 'My Nordea')
|
51
|
+
|
52
|
+
# Use another keychain file
|
53
|
+
n = Nordea.new(:keychain, :label => 'nordea-rb', :keychain_file => '/Users/me/Library/Keychains/login.keychain')
|
54
|
+
|
55
|
+
|
56
|
+
Logging in and out
|
57
|
+
|
58
|
+
# manually login and logout
|
59
|
+
n = Nordea.new(:keychain)
|
60
|
+
n.login
|
61
|
+
# ...
|
62
|
+
n.logout
|
63
|
+
|
64
|
+
|
65
|
+
# If you pass a block, then it will login and logout automatically
|
66
|
+
Nordea.new("1234567890", "1337") do |n|
|
67
|
+
# no need to login or out
|
68
|
+
end
|
69
|
+
|
70
|
+
Listing your accounts
|
71
|
+
|
72
|
+
Nordea.new(...) do |n|
|
73
|
+
puts "you have #{n.accounts.size} account(s)"
|
74
|
+
end
|
75
|
+
|
76
|
+
Accessing your account details
|
77
|
+
|
78
|
+
Nordea.new(...) do |n|
|
79
|
+
account = n.accounts.first
|
80
|
+
another_account = n.accounts['Savings']
|
81
|
+
puts another_account.name # => "Savings"
|
82
|
+
puts account.balance, account.currency, account.to_s
|
83
|
+
|
84
|
+
# You can also access accounts using a regexp:
|
85
|
+
puts n.accounts[/saving/i]
|
86
|
+
end
|
87
|
+
|
88
|
+
Getting a list of transactions for an account
|
89
|
+
|
90
|
+
Nordea.new(...) do |n|
|
91
|
+
savings = n.accounts['Savings']
|
92
|
+
puts savings.transactions.size # => displays 20 per page, paging is not yet supported
|
93
|
+
|
94
|
+
salary_account = n.accounts['Salary']
|
95
|
+
latest = salary_account.transactions.first
|
96
|
+
|
97
|
+
puts latest.text # => the text on the transaction
|
98
|
+
puts latest.amount # => -2000.0
|
99
|
+
puts latest.date # => 2009-01-02
|
100
|
+
puts latest.withdrawal? # => true
|
101
|
+
puts latest.deposit? # => false
|
102
|
+
end
|
103
|
+
|
104
|
+
Transfer money between your own accounts:
|
105
|
+
|
106
|
+
Nordea.new(...) do |n|
|
107
|
+
savings = n.accounts['Savings']
|
108
|
+
salary_account = n.accounts['Salary']
|
109
|
+
|
110
|
+
# transfer 10 SEK from the savings account to the salary account
|
111
|
+
savings.withdraw(10.0, 'SEK', :deposit_to => salary_account)
|
112
|
+
|
113
|
+
# basically the same transaction:
|
114
|
+
salary_account.deposit(10.0, 'SEK', :withdraw_from => savings)
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
## Command Line Tool
|
119
|
+
|
120
|
+
The command line tool is pretty basic right now and it only prints the given account(s) balance(s).
|
121
|
+
Improving it to do more things (like list transactions etc) would just be a matter of polishing the
|
122
|
+
option parser which I throw together quickly.
|
123
|
+
|
124
|
+
Show the balances for the savings account
|
125
|
+
|
126
|
+
./nordea --pnr=1234567890 --pin=0987 Savings
|
127
|
+
|
128
|
+
Show the balances for the accounts named "Savings" and "Salary". Login from keychain
|
129
|
+
|
130
|
+
./nordea --keychain Savings Salary
|
131
|
+
|
132
|
+
|
133
|
+
## TODO
|
134
|
+
|
135
|
+
- Support for transfering money between your own accounts
|
136
|
+
- Clean up the API and class architecture
|
137
|
+
- Add support for paging in transactions list
|
138
|
+
- Build an iPhone optimized web app which uses nordea-rb as backend so we finally can have a better mobile bank
|
139
|
+
|
140
|
+
|
141
|
+
## Credits and license
|
142
|
+
|
143
|
+
By [Martin Ström](http://twitter.com/haraldmartin) under the MIT license:
|
144
|
+
|
145
|
+
> Copyright (c) 2009 Martin Ström
|
146
|
+
>
|
147
|
+
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
148
|
+
> of this software and associated documentation files (the "Software"), to deal
|
149
|
+
> in the Software without restriction, including without limitation the rights
|
150
|
+
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
151
|
+
> copies of the Software, and to permit persons to whom the Software is
|
152
|
+
> furnished to do so, subject to the following conditions:
|
153
|
+
>
|
154
|
+
> The above copyright notice and this permission notice shall be included in
|
155
|
+
> all copies or substantial portions of the Software.
|
156
|
+
>
|
157
|
+
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
158
|
+
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
159
|
+
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
160
|
+
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
161
|
+
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
162
|
+
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
163
|
+
> THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "rake/testtask"
|
3
|
+
|
4
|
+
task :default => :test
|
5
|
+
|
6
|
+
Rake::TestTask.new do |t|
|
7
|
+
t.libs << "test"
|
8
|
+
t.test_files = FileList["test/test_*.rb"]
|
9
|
+
t.verbose = true
|
10
|
+
end
|
11
|
+
|
12
|
+
begin
|
13
|
+
require 'jeweler'
|
14
|
+
Jeweler::Tasks.new do |gemspec|
|
15
|
+
gemspec.name = "nordea-rb"
|
16
|
+
gemspec.summary = "Ruby library for accessing your Nordea Bank account and transferring money between your own accounts."
|
17
|
+
gemspec.description = "Ruby library for accessing your Nordea Bank account and transferring money between your own accounts."
|
18
|
+
gemspec.email = "name@my-domain.se"
|
19
|
+
gemspec.homepage = "http://github.com/haraldmartin/nordea-rb"
|
20
|
+
gemspec.authors = ["Martin Ström"]
|
21
|
+
end
|
22
|
+
Jeweler::GemcutterTasks.new
|
23
|
+
rescue LoadError
|
24
|
+
puts "Jeweler not available. Install it with: gem install jeweler"
|
25
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.0
|
data/bin/nordea
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require File.join(File.dirname(__FILE__), *%w".. lib nordea")
|
5
|
+
|
6
|
+
options, accounts, pnr, command = {}, []
|
7
|
+
|
8
|
+
opts = OptionParser.new do |opts|
|
9
|
+
opts.banner = "Usage: nordea [options]"
|
10
|
+
|
11
|
+
def opts.show_usage
|
12
|
+
puts self
|
13
|
+
exit
|
14
|
+
end
|
15
|
+
|
16
|
+
opts.on('-p', '--pnr=PERSONNUMMER', "Login using the PERSONNUMMER") do |p|
|
17
|
+
pnr = p
|
18
|
+
end
|
19
|
+
|
20
|
+
opts.on('-i', '--pin=PIN', "Login using the PIN code") do |pin|
|
21
|
+
options = pin
|
22
|
+
end
|
23
|
+
|
24
|
+
opts.on('-k', '--keychain', "Get account details from the default keychain. See README how to set this up") do
|
25
|
+
pnr = :keychain
|
26
|
+
end
|
27
|
+
|
28
|
+
opts.on('-f', '--keychain-file=FILE', "Use the keychain FILE") do |file|
|
29
|
+
options[:keychain_file] = file
|
30
|
+
end
|
31
|
+
|
32
|
+
opts.on('-l', '--keychain-label=LABEL', "Look for a keychain item named LABEL") do |label|
|
33
|
+
options[:label] = label
|
34
|
+
end
|
35
|
+
|
36
|
+
opts.on('-b', '--balance', "Print the account's balance") do
|
37
|
+
# right now balance is the only supported command
|
38
|
+
end
|
39
|
+
|
40
|
+
opts.on_tail("-h", "--help", "Shows this help message") { opts.show_usage }
|
41
|
+
opts.on_tail("-v", "--version", "Shows version") do
|
42
|
+
puts Nordea::Version::STRING
|
43
|
+
exit
|
44
|
+
end
|
45
|
+
|
46
|
+
begin
|
47
|
+
opts.order(ARGV) { |a| accounts << a }
|
48
|
+
rescue OptionParser::ParseError => e
|
49
|
+
opts.warn e.message
|
50
|
+
opts.show_usage
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
opts.parse!
|
56
|
+
opts.show_usage unless pnr && accounts.length > 0
|
57
|
+
|
58
|
+
begin
|
59
|
+
Nordea.new(pnr, options) do |n|
|
60
|
+
accounts.each do |account|
|
61
|
+
puts n.accounts[/#{account}/i].to_s
|
62
|
+
end
|
63
|
+
end
|
64
|
+
rescue Nordea::InvalidLogin => e
|
65
|
+
puts "Invalid login: #{e.message}"
|
66
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Nordea
|
2
|
+
class Request
|
3
|
+
HOST_NAME = 'gfs.nb.se'
|
4
|
+
HOST_PORT = 443
|
5
|
+
SCRIPT_NAME = '/bin2/gfskod'
|
6
|
+
USER_AGENT = 'Nokia9110/1.0'
|
7
|
+
|
8
|
+
def initialize(command, extra_params = {})
|
9
|
+
@command, @extra_params = command, extra_params
|
10
|
+
request
|
11
|
+
end
|
12
|
+
|
13
|
+
def connection
|
14
|
+
http = Net::HTTP.new(HOST_NAME, HOST_PORT)
|
15
|
+
http.use_ssl = true
|
16
|
+
http
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse_xml
|
20
|
+
Hpricot.XML(response)
|
21
|
+
end
|
22
|
+
|
23
|
+
def request
|
24
|
+
res = connection.post(SCRIPT_NAME, query, headers)
|
25
|
+
@result = res.body
|
26
|
+
end
|
27
|
+
|
28
|
+
def headers
|
29
|
+
{ "User-Agent" => USER_AGENT }
|
30
|
+
end
|
31
|
+
|
32
|
+
def response
|
33
|
+
@result
|
34
|
+
end
|
35
|
+
|
36
|
+
def query
|
37
|
+
@extra_params.merge({ "OBJECT" => @command }).
|
38
|
+
inject([]) { |all, (key, value)| all << "#{key}=#{value}" }.
|
39
|
+
join("&")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module Nordea
|
2
|
+
class Account < Resource
|
3
|
+
def initialize(fields = {}, command_params = {}, session = nil)
|
4
|
+
@name, @balance, @currency, @index = fields[:name], fields[:balance], fields[:currency], fields[:index]
|
5
|
+
@command_params = command_params
|
6
|
+
@session = session
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_accessor :name, :balance, :currency, :index, :session
|
10
|
+
|
11
|
+
def account_type_name; 'konton' end
|
12
|
+
|
13
|
+
def to_command_params
|
14
|
+
@command_params
|
15
|
+
end
|
16
|
+
|
17
|
+
# TODO move shared transaction fetching to Nordea::Resource and call super
|
18
|
+
def transactions(reload = false)
|
19
|
+
@transactions = nil if reload
|
20
|
+
@transactions ||= begin
|
21
|
+
doc = session.request(Commands::TRANSACTIONS, to_command_params).parse_xml
|
22
|
+
doc.search('go[@href="#trans"]').inject([]) do |all, node|
|
23
|
+
all << Transaction.new_from_xml(node)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
"#{name}: #{balance_to_s} #{currency}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def balance_to_s(decimals = 2)
|
33
|
+
%Q{%.#{decimals}f} % balance
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.new_from_xml(xml_node, session)
|
37
|
+
xml_node = Hpricot(xml_node) unless xml_node.class.to_s =~ /^Hpricot::/
|
38
|
+
xml_node = xml_node.at("anchor") unless xml_node.name == 'anchor'
|
39
|
+
new _setup_fields(xml_node), _setup_command_params(xml_node), session
|
40
|
+
end
|
41
|
+
|
42
|
+
def withdraw(amount, currency = 'SEK', options = {})
|
43
|
+
options.symbolize_keys!
|
44
|
+
to = options[:deposit_to]
|
45
|
+
|
46
|
+
raise ArgumentError, "options[:deposit_to] must be a Nordea::Account" unless to.is_a?(Nordea::Account)
|
47
|
+
|
48
|
+
currency, amount = currency.to_s, amount.to_s.sub(".", ",")
|
49
|
+
from_account_info = [index, currency, name].join(":")
|
50
|
+
to_account_info = [to.index, to.currency, to.name].join(":")
|
51
|
+
|
52
|
+
params = { :from_account_info => from_account_info,
|
53
|
+
:to_account_info => to_account_info,
|
54
|
+
:amount => amount,
|
55
|
+
:currency_code => currency }
|
56
|
+
|
57
|
+
session.request(Nordea::Commands::TRANSFER_TO_OWN_ACCOUNT_PHASE_1, params)
|
58
|
+
session.request(Nordea::Commands::TRANSFER_TO_OWN_ACCOUNT_PHASE_2, params)
|
59
|
+
session.request(Nordea::Commands::TRANSFER_TO_OWN_ACCOUNT_PHASE_3, {
|
60
|
+
:currency_code => currency,
|
61
|
+
:from_account_number => index,
|
62
|
+
:from_account_name => name,
|
63
|
+
:to_account_number => to.index,
|
64
|
+
:to_account_name => to.name,
|
65
|
+
:amount => amount,
|
66
|
+
:exchange_rate => "0",
|
67
|
+
:from_currency_code => currency,
|
68
|
+
:to_currency_code => currency
|
69
|
+
})
|
70
|
+
|
71
|
+
session.accounts(true)
|
72
|
+
end
|
73
|
+
|
74
|
+
def deposit(amount, currency = 'SEK', options = {})
|
75
|
+
options.symbolize_keys!
|
76
|
+
from = options.delete(:withdraw_from)
|
77
|
+
|
78
|
+
raise ArgumentError unless from.is_a?(Nordea::Account)
|
79
|
+
|
80
|
+
from.withdraw(amount, currency, options.merge(:deposit_to => self))
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def self._setup_fields(xml_node)
|
86
|
+
name = xml_node.at("postfield[@name='account_name']")['value']
|
87
|
+
currency = xml_node.at("postfield[@name='account_currency_code']")['value']
|
88
|
+
index = xml_node.at("postfield[@name='account_index']")['value']
|
89
|
+
balance = xml_node.next_sibling.next
|
90
|
+
{ :name => name, :currency => currency,
|
91
|
+
:balance => Support.dirty_currency_string_to_f(balance), :index => index }
|
92
|
+
end
|
93
|
+
|
94
|
+
def self._setup_command_params(xml_node)
|
95
|
+
command_params = (xml_node/'postfield').to_a.inject({}) do |all, postfield|
|
96
|
+
name = postfield['name']
|
97
|
+
all[name] = postfield['value'] unless name =~ /sid|OBJECT/
|
98
|
+
all
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'nordea/resources/resource'
|
2
|
+
require 'nordea/resources/account'
|
3
|
+
require 'nordea/resources/card'
|
4
|
+
require 'nordea/resources/fund'
|
5
|
+
require 'nordea/resources/loan'
|
6
|
+
|
7
|
+
class ResourceCollection < Array
|
8
|
+
def [](account_name)
|
9
|
+
if account_name.is_a?(String)
|
10
|
+
detect { |e| e.name == account_name }
|
11
|
+
elsif account_name.is_a?(Regexp)
|
12
|
+
detect { |e| e.name =~ account_name }
|
13
|
+
else
|
14
|
+
super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Nordea
|
2
|
+
class Session
|
3
|
+
attr_accessor :pnr, :pin, :token
|
4
|
+
attr_reader :num_requests
|
5
|
+
|
6
|
+
def initialize(pnr, pin, &block)
|
7
|
+
@pnr, @pin = pnr, pin
|
8
|
+
@num_requests = 0
|
9
|
+
if block_given?
|
10
|
+
login
|
11
|
+
yield self
|
12
|
+
logout
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def login
|
17
|
+
doc = request(Commands::LOGIN_PHASE_1, { "kundnr" => pnr, "pinkod" => pin, "sid" => "0" }).parse_xml
|
18
|
+
begin
|
19
|
+
@token = (doc/'*[@name=sid]').attr("value")
|
20
|
+
contract = (doc/'postfield[@name=contract]').attr("value")
|
21
|
+
rescue Exception => e
|
22
|
+
if node = doc.at("card[@title='Fel']")
|
23
|
+
error_message = node.at("p").inner_text.strip
|
24
|
+
else
|
25
|
+
error_message = ""
|
26
|
+
end
|
27
|
+
raise Nordea::InvalidLogin, error_message
|
28
|
+
end
|
29
|
+
request Commands::LOGIN_PHASE_2, "bank" => "mobile_light", "no_prev" => "1", "contract" => contract
|
30
|
+
end
|
31
|
+
|
32
|
+
alias_method :login!, :login
|
33
|
+
|
34
|
+
def logout
|
35
|
+
request Commands::LOGOUT
|
36
|
+
end
|
37
|
+
|
38
|
+
alias_method :logout!, :logout
|
39
|
+
|
40
|
+
def request(command, extra_params = {})
|
41
|
+
extra_params.symbolize_keys!
|
42
|
+
extra_params.merge!(:sid => token) unless extra_params.has_key?(:sid)
|
43
|
+
@num_requests += 1
|
44
|
+
Nordea::Request.new(command, extra_params)
|
45
|
+
end
|
46
|
+
|
47
|
+
def accounts(reload = false)
|
48
|
+
@accounts = nil if reload
|
49
|
+
@accounts ||= setup_resources(Account)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def setup_resources(klass)
|
55
|
+
type = klass.new.account_type_name
|
56
|
+
doc = request(Commands::RESOURCE, "account_type_name" => type).parse_xml
|
57
|
+
(doc/"go postfield[@name='OBJECT'][@value='#{Commands::TRANSACTIONS}']").
|
58
|
+
inject(ResourceCollection.new) do |all, field|
|
59
|
+
all << klass.new_from_xml(field.parent.parent, self)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Nordea
|
2
|
+
class Support
|
3
|
+
def self.dirty_currency_string_to_f(string)
|
4
|
+
string.to_s.strip.gsub(/(\302|\240)/, "").gsub('.', '').gsub(',', '.').delete('SEK').to_f
|
5
|
+
end
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def quietly
|
10
|
+
v = $VERBOSE
|
11
|
+
$VERBOSE = nil
|
12
|
+
yield
|
13
|
+
ensure
|
14
|
+
$VERBOSE = v
|
15
|
+
end
|
16
|
+
|
17
|
+
# quietly { OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE }
|
18
|
+
|
19
|
+
class Net::HTTP
|
20
|
+
alias_method :old_initialize, :initialize
|
21
|
+
def initialize(*args)
|
22
|
+
old_initialize(*args)
|
23
|
+
@ssl_context = OpenSSL::SSL::SSLContext.new
|
24
|
+
@ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Symbol
|
29
|
+
def to_proc
|
30
|
+
Proc.new { |obj, *args| obj.send(self, *args) }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# from ActiveSupport
|
35
|
+
class Hash
|
36
|
+
def stringify_keys
|
37
|
+
inject({}) do |options, (key, value)|
|
38
|
+
options[key.to_s] = value
|
39
|
+
options
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def stringify_keys!
|
44
|
+
keys.each do |key|
|
45
|
+
self[key.to_s] = delete(key)
|
46
|
+
end
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
def symbolize_keys
|
51
|
+
inject({}) do |options, (key, value)|
|
52
|
+
options[(key.to_sym rescue key) || key] = value
|
53
|
+
options
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def symbolize_keys!
|
58
|
+
self.replace(self.symbolize_keys)
|
59
|
+
end
|
60
|
+
|
61
|
+
alias_method :to_options, :symbolize_keys
|
62
|
+
alias_method :to_options!, :symbolize_keys!
|
63
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module Nordea
|
4
|
+
class Transaction
|
5
|
+
def initialize(date, amount, text)
|
6
|
+
@date, @amount, @text = date, amount, text
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_accessor :date, :amount, :text
|
10
|
+
|
11
|
+
def withdrawal?
|
12
|
+
amount < 0
|
13
|
+
end
|
14
|
+
|
15
|
+
def deposit?
|
16
|
+
amount > 0
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.new_from_xml(xml)
|
20
|
+
node = xml.is_a?(String) ? Hpricot.XML(xml) : xml
|
21
|
+
date = Date.parse(node.at("setvar[@name='date']")['value'])
|
22
|
+
amount = Support.dirty_currency_string_to_f(node.at("setvar[@name='amount']")['value'])
|
23
|
+
text = node.at("setvar[@name='text']")['value']
|
24
|
+
new date, amount, text
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|