anz_bank_client 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c6bda86e04ac9e4f972bd220896ed3b153e090c0c9c07a90da02fbac2a478a78
4
+ data.tar.gz: 1a068361988675e5e211e3b93b608f3b0a7dd63f44f8ee9d64b5cecb278ba575
5
+ SHA512:
6
+ metadata.gz: 807b91ec4851c8aa8c82ad9b9b17d0db013be7cbfb6a5935e4173d2cd1939284f7fe5f3d42eb8eb3add90988d0de1fc52072badabcac630e635fc6e180b828d9
7
+ data.tar.gz: 9fa8ab3bb45e89bba0d75cc533c9c6a2ae0434db79f9d9333bec24bb5987af4bbf34fa3afb3b5e4994b8aa0481eaf3ae82db21b35f9065cfc980238f91dbcd06
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ .idea
10
+ *.gem
11
+ .env
12
+ *.log
13
+
data/.rubocop.yml ADDED
@@ -0,0 +1,50 @@
1
+ AllCops:
2
+ TargetRubyVersion: 3.0
3
+
4
+ Style/StringLiterals:
5
+ Enabled: true
6
+ EnforcedStyle: double_quotes
7
+
8
+ Style/StringLiteralsInInterpolation:
9
+ Enabled: true
10
+ EnforcedStyle: double_quotes
11
+
12
+ Layout/LineLength:
13
+ Max: 120
14
+ IgnoredPatterns: ['Mozilla/5.0', 'https://']
15
+
16
+ Metrics/AbcSize:
17
+ Enabled: false
18
+
19
+ Metrics/MethodLength:
20
+ Enabled: false
21
+
22
+ Style/TrailingCommaInHashLiteral:
23
+ EnforcedStyleForMultiline: consistent_comma
24
+
25
+ Style/TrailingCommaInArguments:
26
+ EnforcedStyleForMultiline: consistent_comma
27
+
28
+ Style/RescueModifier:
29
+ Enabled: false
30
+
31
+ Style/FormatString:
32
+ EnforcedStyle: percent
33
+
34
+ Metrics/ClassLength:
35
+ Enabled: false
36
+
37
+ Style/IfUnlessModifier:
38
+ Enabled: false
39
+
40
+ Metrics/CyclomaticComplexity:
41
+ Enabled: false
42
+
43
+ Style/GuardClause:
44
+ Enabled: false
45
+
46
+ Style/Documentation:
47
+ Enabled: false
48
+
49
+ Style/FrozenStringLiteralComment:
50
+ Enabled: false
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in anz_bank_client.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "minitest", "~> 5.0"
11
+
12
+ gem "rubocop", "~> 1.4.0"
13
+
14
+ gem "table_print"
15
+
16
+ gem "dotenv"
17
+
18
+ gem "thor"
data/Gemfile.lock ADDED
@@ -0,0 +1,68 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ anz_bank_client (0.1.1)
5
+ faraday (~> 2.7)
6
+ faraday-cookie_jar (~> 0.0.7)
7
+ faraday-follow_redirects (~> 0.3.0)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ ast (2.4.2)
13
+ domain_name (0.6.20240107)
14
+ dotenv (2.8.1)
15
+ faraday (2.9.0)
16
+ faraday-net_http (>= 2.0, < 3.2)
17
+ faraday-cookie_jar (0.0.7)
18
+ faraday (>= 0.8.0)
19
+ http-cookie (~> 1.0.0)
20
+ faraday-follow_redirects (0.3.0)
21
+ faraday (>= 1, < 3)
22
+ faraday-net_http (3.1.0)
23
+ net-http
24
+ http-cookie (1.0.5)
25
+ domain_name (~> 0.5)
26
+ minitest (5.20.0)
27
+ net-http (0.4.1)
28
+ uri
29
+ parallel (1.23.0)
30
+ parser (3.2.2.4)
31
+ ast (~> 2.4.1)
32
+ racc
33
+ racc (1.7.3)
34
+ rainbow (3.1.1)
35
+ rake (13.1.0)
36
+ regexp_parser (2.8.2)
37
+ rexml (3.2.6)
38
+ rubocop (1.4.2)
39
+ parallel (~> 1.10)
40
+ parser (>= 2.7.1.5)
41
+ rainbow (>= 2.2.2, < 4.0)
42
+ regexp_parser (>= 1.8)
43
+ rexml
44
+ rubocop-ast (>= 1.1.1)
45
+ ruby-progressbar (~> 1.7)
46
+ unicode-display_width (>= 1.4.0, < 2.0)
47
+ rubocop-ast (1.30.0)
48
+ parser (>= 3.2.1.0)
49
+ ruby-progressbar (1.13.0)
50
+ table_print (1.5.7)
51
+ thor (1.3.0)
52
+ unicode-display_width (1.8.0)
53
+ uri (0.13.0)
54
+
55
+ PLATFORMS
56
+ x86_64-linux
57
+
58
+ DEPENDENCIES
59
+ anz_bank_client!
60
+ dotenv
61
+ minitest (~> 5.0)
62
+ rake (~> 13.0)
63
+ rubocop (~> 1.4.0)
64
+ table_print
65
+ thor
66
+
67
+ BUNDLED WITH
68
+ 2.2.3
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 George Dewar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # ANZ Bank Client
2
+
3
+ This gem is a client for the private, undocumented API behind ANZ New Zealand's personal internet banking website. It is
4
+ not affiliated with ANZ Bank in any way and is intended only for personal use.
5
+
6
+ It can be used in a Ruby script or application, or alternatively as a command-line tool (anzcli).
7
+
8
+ It is currently capable of logging in and retrieving account balances and transaction history, but could be easily
9
+ extended to do other things. Please note that this gem is not officially supported by ANZ Bank and may stop working at
10
+ any time due to changes to how their internet banking application works. ANZ's terms and conditions for their internet
11
+ banking product may also change at any time, and you should ensure that you are complying with them if you use this gem.
12
+
13
+ ## Use as a command line tool
14
+
15
+ Install the gem globally with `gem install anz_bank_client` and then run `anzcli` to get started.
16
+
17
+ You can use the `help` option to see a list of available commands and options.
18
+
19
+ ```
20
+ anzcli commands:
21
+ anzcli help [COMMAND] # Describe available commands or one specific command
22
+ anzcli login # Login to ANZ
23
+ anzcli logout # Logout
24
+ anzcli ls-accounts # List accounts
25
+ anzcli ls-transactions ACCOUNT_NUMBER # List transactions
26
+
27
+ Options:
28
+ -f, [--format=FORMAT] # Output format
29
+ # Default: table
30
+ # Possible values: table, json
31
+ ```
32
+
33
+ The remaining sections are about using the gem in a Ruby script or application.
34
+
35
+ ## Installation
36
+
37
+ Add this line to your application's Gemfile:
38
+
39
+ ```ruby
40
+ gem 'anz_bank_client'
41
+ ```
42
+
43
+ And then execute:
44
+
45
+ $ bundle install
46
+
47
+ Or install it yourself as:
48
+
49
+ $ gem install anz_bank_client
50
+
51
+ ## Usage
52
+
53
+ First, create an instance of the client.
54
+
55
+ ```ruby
56
+ @session = AnzBankClient::Session.new
57
+ ```
58
+
59
+ Then, log in.
60
+
61
+ ```ruby
62
+ @session.login(customer_number, password)
63
+ ```
64
+
65
+ You can then list accounts and transactions.
66
+
67
+ ```ruby
68
+ accounts = @session.list_accounts
69
+
70
+ first_account_number = accounts.first[:accountNo] # e.g. 01-1234-5678901-00
71
+ start_date = Date.today.prev_month.to_s
72
+ end_date = Date.today.to_s
73
+ transactions = @session.list_transactions(first_account_number, start_date, end_date)
74
+ ```
75
+
76
+ Finally, log out.
77
+
78
+ ```ruby
79
+ @session.logout
80
+ ```
81
+
82
+ It's very important to log out, as ANZ's internet banking application has a maximum number of concurrent sessions and
83
+ you could lock yourself out of your account for a while if you create too many of them without logging out.
84
+
85
+ ## Development
86
+
87
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
88
+
89
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
90
+
91
+ ## Contributing
92
+
93
+ Bug reports and pull requests are welcome on GitHub at https://github.com/GeorgeDewar/anz_bank_client.
94
+
95
+ ## License
96
+
97
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ end
11
+
12
+ require "rubocop/rake_task"
13
+
14
+ RuboCop::RakeTask.new
15
+
16
+ task default: %i[test rubocop]
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/anz_bank_client/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "anz_bank_client"
7
+ spec.version = AnzBankClient::VERSION
8
+ spec.authors = ["George Dewar"]
9
+ spec.email = ["george@dewar.co.nz"]
10
+
11
+ spec.summary = "Fetch transaction data from ANZ Bank (New Zealand)"
12
+ spec.description = "This gem can log into ANZ online banking and fetch account and transaction data"
13
+ spec.homepage = "https://github.com/GeorgeDewar/anz_bank_client"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = Gem::Requirement.new(">= 3.0.0")
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = spec.homepage
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
23
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
24
+ end
25
+ spec.bindir = "exe"
26
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ["lib"]
28
+
29
+ # Uncomment to register a new dependency of your gem
30
+ spec.add_dependency "faraday", "~> 2.7"
31
+ spec.add_dependency "faraday-cookie_jar", "~> 0.0.7"
32
+ spec.add_dependency "faraday-follow_redirects", "~> 0.3.0"
33
+
34
+ # For more information and examples about making a new gem, checkout our
35
+ # guide at: https://bundler.io/guides/creating_gem.html
36
+ end
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "anz_bank_client"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/anzcli ADDED
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env ruby
2
+ require "anz_bank_client"
3
+ require "readline"
4
+ require "optparse"
5
+ require "thor"
6
+ require "table_print"
7
+ require "io/console"
8
+ require "faraday"
9
+
10
+ class AnzCli < Thor
11
+ def initialize(args = [], local_options = {}, config = {})
12
+ super(args, local_options, config)
13
+ @session = AnzBankClient::Session.new
14
+ end
15
+
16
+ package_name "anzcli"
17
+
18
+ class_option :format, aliases: "-f", desc: "Output format", default: "table", enum: %w[table json]
19
+
20
+ desc "login", "Login to ANZ"
21
+ long_desc <<~DESC
22
+ Login to ANZ. If the environment variables ANZ_CUSTOMER_NO and ANZ_PASSWORD are set, they will be used. Otherwise,
23
+ you will be prompted for your customer number and password. Session details are saved in ~/.anzbank.yaml and used
24
+ for future commands.
25
+ DESC
26
+ def login
27
+ if ENV["ANZ_CUSTOMER_NO"]
28
+ @session.login(ENV["ANZ_CUSTOMER_NO"], ENV["ANZ_PASSWORD"])
29
+ else
30
+ print "Customer number: "
31
+ customer_no = gets.chomp
32
+ print "Password: "
33
+ password = $stdin.noecho(&:gets).chomp
34
+ @session.login(customer_no, password)
35
+ end
36
+ cookies = @session.export
37
+ File.open("#{Dir.home}/.anzbank.yaml", "w") { |f| f.write(cookies) }
38
+ end
39
+
40
+ desc "ls-accounts", "List accounts"
41
+ def ls_accounts
42
+ check_session
43
+ if options[:format] == "json"
44
+ puts @session.list_accounts.to_json
45
+ else
46
+ tp @session.list_accounts
47
+ end
48
+ end
49
+
50
+ desc "ls-transactions ACCOUNT_NUMBER", "List transactions"
51
+ method_option :from, desc: "From date (YYYY-MM-DD)", default: Date.today.prev_month.to_s
52
+ method_option :to, desc: "To date (YYYY-MM-DD)", default: Date.today.to_s
53
+ def ls_transactions(account)
54
+ check_session
55
+ transactions = @session.list_transactions(account, options[:from], options[:to])
56
+ if options[:format] == "json"
57
+ puts transactions.to_json
58
+ else
59
+ txns = transactions.map do |t|
60
+ {
61
+ date: DateTime.parse(t[:createdDateTime] || t[:date]).strftime("%Y-%m-%d"),
62
+ time: t[:createdDateTime] ? DateTime.parse(t[:createdDateTime]).strftime("%H:%M:%S") : "",
63
+ posted_date: t[:postedDate],
64
+ type: t[:type],
65
+ description1: t[:details][0],
66
+ description2: t[:details][1],
67
+ amount: "% 12.2f" % t[:amount],
68
+ balance: "% 12.2f" % t[:balance],
69
+ }
70
+ end
71
+ tp txns
72
+ end
73
+ end
74
+
75
+ desc "logout", "Logout"
76
+ def logout
77
+ cookies = File.read("#{Dir.home}/.anzbank.yaml") rescue nil
78
+ if cookies
79
+ @session.load(cookies)
80
+ @session.logout
81
+ File.delete("#{Dir.home}/.anzbank.yaml") rescue nil
82
+ else
83
+ puts "No session to log out from"
84
+ end
85
+ end
86
+
87
+ def self.exit_on_failure?
88
+ true
89
+ end
90
+
91
+ private
92
+
93
+ # Fetch the session details if they exist, otherwise login
94
+ def check_session
95
+ cookies = File.read("#{Dir.home}/.anzbank.yaml") rescue nil
96
+ if cookies
97
+ @session.load(cookies)
98
+ else
99
+ login
100
+ end
101
+ end
102
+ end
103
+
104
+ AnzCli.start(ARGV)
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AnzBankClient
4
+ VERSION = "0.1.1"
5
+ end
@@ -0,0 +1,206 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require "dotenv/load"
5
+ rescue LoadError
6
+ # Dotenv is not available, so move on without loading it. It's only used for development.
7
+ end
8
+ require_relative "anz_bank_client/version"
9
+ require "faraday"
10
+ require "faraday-cookie_jar"
11
+ require "faraday/follow_redirects"
12
+
13
+ module AnzBankClient
14
+ class Error < StandardError; end
15
+
16
+ def self.login(username, password)
17
+ session = Session.new
18
+ session.login(username, password)
19
+ session
20
+ end
21
+
22
+ class Session
23
+ def initialize
24
+ @logger = Logger.new $stderr
25
+ @logger.level = Logger::INFO
26
+
27
+ @cookie_jar = HTTP::CookieJar.new
28
+ @user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36"
29
+
30
+ # Set IBCookieDetect cookie so it knows we have cookies enabled
31
+ @cookie_jar.add(HTTP::Cookie.new(name: "IBCookieDetect", value: "1", domain: "digital.anz.co.nz", path: "/"))
32
+ @client = Faraday.new(
33
+ headers: { "User-Agent" => @user_agent },
34
+ # proxy: "http://Thinkbook.local:8080",
35
+ # :ssl => {:verify => false}
36
+ ) do |builder|
37
+ builder.response :follow_redirects
38
+ builder.use Faraday::CookieJar, jar: @cookie_jar
39
+ # builder.response :logger
40
+ builder.adapter Faraday.default_adapter
41
+ end
42
+ end
43
+
44
+ # Write the cookie jar out to a string so that the session can be restored later
45
+ def export
46
+ cookies = StringIO.new
47
+ @cookie_jar.save(cookies, format: :yaml, session: true)
48
+ {
49
+ cookies: cookies.string,
50
+ initialise_response: @initialise_response,
51
+ }.to_json
52
+ end
53
+
54
+ # Load the cookie jar from a string to restore a session
55
+ def load(str)
56
+ json = JSON.parse(str)
57
+ @initialise_response = json["initialise_response"]
58
+ cookies = StringIO.new
59
+ cookies.write(json["cookies"])
60
+ cookies.rewind
61
+ @cookie_jar.load(cookies, format: :yaml)
62
+ end
63
+
64
+ def login(username, password)
65
+ @logger.info "Fetching login page"
66
+
67
+ login_page = @client.get("https://digital.anz.co.nz/preauth/web/service/login")
68
+ if login_page.status != 200
69
+ raise "Error getting login page: #{login_page.status} #{login_page.body}"
70
+ end
71
+
72
+ @encryption_key = login_page.body.match(/encryptionKey: "(.*)",/)[1]
73
+ @encryption_key_id = login_page.body.match(/encryptionKeyId: "(.*)",/)[1]
74
+
75
+ # Encrypt password using encryption key
76
+ encrypted_password = encrypt_password(password).strip
77
+
78
+ # Log in
79
+ @logger.info "Logging in as #{username}"
80
+ response = @client.post("https://digital.anz.co.nz/preauth/web/service/login") do |req|
81
+ req.headers = {
82
+ "User-Agent" => @user_agent,
83
+ "Accept" => "application/json",
84
+ "Content-Type" => "application/json",
85
+ }
86
+ req.body = {
87
+ userId: username,
88
+ password: encrypted_password,
89
+ "referrer": "",
90
+ "firstPage": "",
91
+ "publicKeyId": @encryption_key_id,
92
+ }.to_json
93
+ end
94
+ if JSON.parse(response.body)["code"] != "success"
95
+ raise "Error logging in: #{response.status}\n\n#{response.body}"
96
+ end
97
+
98
+ @logger.info "Fetching session details"
99
+ response = @client.get("https://secure.anz.co.nz/IBCS/service/session?referrer=https%3A%2F%2Fsecure.anz.co.nz%2F")
100
+ if response.status != 200
101
+ raise "Session setup failed with status #{response.status}\n\n#{response.body}"
102
+ end
103
+
104
+ csrf_token_match = response.body.match(/sessionCsrfToken *= *"(.*)";/)
105
+ unless csrf_token_match
106
+ raise "Could not find CSRF token in page body:\n\n#{response.body}"
107
+ end
108
+
109
+ csrf_token = csrf_token_match[1]
110
+ @logger.info "CSRF Token: #{csrf_token}"
111
+
112
+ @logger.info "Getting initial details"
113
+ response = @client.get("https://secure.anz.co.nz/IBCS/service/home/initialise")
114
+ raise "Error getting initial details: #{response.status}\n\n#{response.body}" unless response.status == 200
115
+
116
+ @initialise_response = JSON.parse(response.body)
117
+ end
118
+
119
+ def list_accounts
120
+ @initialise_response["viewableAccounts"].map do |account|
121
+ balance = account.dig("accountBalance", "amount")
122
+ overdrawn = balance && (
123
+ account.dig("accountBalance", "indicator") == "overdrawn" || account["isLoan"] || account["isCreditCard"]
124
+ )
125
+ normalised_balance = if balance
126
+ overdrawn ? -balance : balance
127
+ end
128
+
129
+ {
130
+ accountNo: account["accountNo"],
131
+ nickname: account["nicknameEscaped"],
132
+ accountType: account["productDescription"],
133
+ customerName: account["accountOwnerName"],
134
+ accountBalance: normalised_balance,
135
+ availableFunds: account.dig("availableFunds", "amount"),
136
+ isLiabilityType: account["isLoan"] || account["isCreditCard"],
137
+ }
138
+ end
139
+ end
140
+
141
+ # Fetches transactions for an account
142
+ #
143
+ # @param account_no the account number (e.g. 01-1234-1234567-00)
144
+ # @param start_date in iso8601 format
145
+ # @param end_date in iso8601 format
146
+ def list_transactions(account_no, start_date, end_date)
147
+ @logger.info "Getting transactions for account #{account_no} from #{start_date} to #{end_date}"
148
+ account_uuid = @initialise_response["viewableAccounts"]
149
+ .find { |account| account["accountNo"] == account_no }["accountUuid"]
150
+ raise "Could not find account #{account_no}" unless account_uuid
151
+
152
+ response = @client.get("https://secure.anz.co.nz/IBCS/service/api/transactions?account=#{account_uuid}&ascending=false&from=#{start_date}&order=postdate&to=#{end_date}")
153
+ raise "Error getting transactions: #{response.status}\n\n#{response.body}" unless response.status == 200
154
+
155
+ response_json = JSON.parse(response.body)
156
+ response_json["transactions"].map do |transaction|
157
+ {
158
+ date: transaction["date"],
159
+ postedDate: transaction["postedDate"],
160
+ details: transaction["details"],
161
+ amount: transaction["amount"]["amount"],
162
+ currencyCode: transaction["amount"]["currencyCode"],
163
+ type: transaction["type"],
164
+ balance: transaction.dig("balance", "amount"),
165
+ createdDateTime: transaction["createdDateTime"],
166
+ }
167
+ end
168
+ end
169
+
170
+ def logout
171
+ @logger.info "Logging out"
172
+ response = @client.get("https://secure.anz.co.nz/IBCS/service/goodbye")
173
+ if response.status != 200
174
+ raise "Error logging out: #{response.status}\n\n#{response.body}"
175
+ end
176
+ end
177
+
178
+ private
179
+
180
+ # Method to convert a base64 encoded key into PEM format
181
+ def convert_to_pem_format(key_str)
182
+ trimmed_key = key_str.strip
183
+
184
+ header = "-----BEGIN PUBLIC KEY-----"
185
+ footer = "-----END PUBLIC KEY-----"
186
+
187
+ # Split the key into 64-character lines
188
+ split_lines = trimmed_key.scan(/.{1,64}/).join("\n")
189
+ "#{header}\n#{split_lines}\n#{footer}"
190
+ end
191
+
192
+ def encrypt_password(password)
193
+ # Convert the key to PEM format
194
+ pem_formatted_key = convert_to_pem_format(@encryption_key)
195
+
196
+ # Create a public key object from the PEM-formatted string
197
+ public_key = OpenSSL::PKey::RSA.new(pem_formatted_key)
198
+
199
+ # Encrypt the message using the public key and PKCS1 padding
200
+ encrypted_password = public_key.public_encrypt(password, OpenSSL::PKey::RSA::PKCS1_PADDING)
201
+
202
+ # Encode the encrypted message with base64
203
+ Base64.encode64(encrypted_password).gsub("\n", "")
204
+ end
205
+ end
206
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: anz_bank_client
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - George Dewar
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-02-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday-cookie_jar
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.0.7
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.0.7
41
+ - !ruby/object:Gem::Dependency
42
+ name: faraday-follow_redirects
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.3.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.3.0
55
+ description: This gem can log into ANZ online banking and fetch account and transaction
56
+ data
57
+ email:
58
+ - george@dewar.co.nz
59
+ executables:
60
+ - anzcli
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - ".gitignore"
65
+ - ".rubocop.yml"
66
+ - Gemfile
67
+ - Gemfile.lock
68
+ - LICENSE.txt
69
+ - README.md
70
+ - Rakefile
71
+ - anz_bank_client.gemspec
72
+ - bin/console
73
+ - bin/setup
74
+ - exe/anzcli
75
+ - lib/anz_bank_client.rb
76
+ - lib/anz_bank_client/version.rb
77
+ homepage: https://github.com/GeorgeDewar/anz_bank_client
78
+ licenses:
79
+ - MIT
80
+ metadata:
81
+ homepage_uri: https://github.com/GeorgeDewar/anz_bank_client
82
+ source_code_uri: https://github.com/GeorgeDewar/anz_bank_client
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: 3.0.0
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubygems_version: 3.2.3
99
+ signing_key:
100
+ specification_version: 4
101
+ summary: Fetch transaction data from ANZ Bank (New Zealand)
102
+ test_files: []