nordea-rb 0.2.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.
- 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
|