package Finance::Bank::ABSA; use strict; use Carp; our $VERSION = '0.01'; use WWW::Mechanize; use HTML::TokeParser; sub check_ABSA_balance { my ($class, %opts) = @_; my @accounts; croak "Must provide a security code" unless exists $opts{seccode}; croak "Must provide a banking id" unless exists $opts{bankingid}; croak "Must provide a password" unless exists $opts{mypassword}; my $self = bless { %opts }, $class; my $agent = WWW::Mechanize->new(); $agent->get("https://vs1.absa.co.za/ibs/UserAuthentication.do"); # Filling in the login form. $agent->form("theForm"); $agent->field("AccessAccount", $opts{bankingid}); $agent->field("PIN", $opts{seccode}); $agent->click("button_processAuthenticate"); die unless ($agent->success); my @secletter = split(//, $opts{mypassword}); $agent->form("AuthPasswordForm"); $agent->field("pwdDgt0", @secletter[0]); $agent->field("pwdDgt1", @secletter[1]); $agent->field("pwdDgt2", @secletter[2]); $agent->field("pwdDgt3", @secletter[3]); $agent->field("pwdDgt4", @secletter[4]); $agent->field("pwdDgt5", @secletter[5]); $agent->field("pwdDgt6", @secletter[6]); $agent->click("button_processAuthPassword"); die unless ($agent->success); # Now we have the data, we need to parse it. This is fragile. my $content = $agent->{content}; my ($split1, $split2) = split /\Balance Enquiries/, $content; my ($content, $split2) = split /Click on the account number for a statement/, $split2; my $stream = HTML::TokeParser->new(\$content) or die "$!"; $stream->get_tag("table"); $stream->get_tag("tr"); $stream->get_tag("tr"); $stream->get_tag("tr"); #print "Hello"; while (my $token = $stream->get_tag("tr")) { $token = $stream->get_tag("td"); if ($token->[1]{width} eq "5") { $stream->get_tag("td"); # We have an account! my $accountname = $stream->get_trimmed_text("/td"); $stream->get_tag("td"); my $accountnumber = $stream->get_trimmed_text("/td"); $stream->get_tag("td"); my $accountbalance = $stream->get_trimmed_text("/td"); my @strarray = split(//, $accountbalance); $stream->get_tag("td"); my $accountavailable = $stream->get_trimmed_text("/td"); # Octal 240 is used from HTML3.2 spec to replace   # See Entities.pm under your perl vendor tree # I choose to replace it here to cater for later formatting correctly. $accountbalance =~ s/,/./; $accountbalance =~ s/\240//g; $accountavailable =~ s/[, ]/./g; $accountavailable =~ s/\240//g; $accountname =~ s/\240//g; push @accounts, { balance => $accountbalance, name => $accountname, available => $accountavailable, account => $accountnumber }; } } return @accounts; } 1; __END__ =head1 NAME Finance::Bank::ABSA - Check your ABSA bank accounts from Perl =head1 SYNOPSIS use Finance::Bank::ABSA; my @ABSAaccounts = Finance::Bank::ABSA->check_ABSA_balance( bankingid => "xxxxxxxxx", mypassword => "xxxxxxx", seccode => "xxxxxx" ); foreach (@ABSAaccounts) { printf " %-21s : %-18s : ZAR %12.2f : ZAR %12.2f\n", $_->{name}, $_->{account}, $_->{balance}, $_->{available}; } =head1 DESCRIPTION This module provides a rudimentary interface to the South African ABSA online banking system at C which is where C redirects to. =head1 DEPENDENCIES You will need either C or C installed for HTTPS support to work with LWP. This module also depends on C and C for screen-scraping. =head1 CLASS METHODS check_ABSA_balance(bankingid => $u, mypassword => $p, seccode => $s) Return an array of account hashes, one for each of your bank accounts. =head1 ACCOUNT HASH KEYS $ac->name $ac->account $ac->balance $ac->available Returns the account name, account number, real balance and available balance which includes overdraft/creditlines. =head1 IMPORTANT WARNING The current version of this module ASSUMES a seven letter password. Furthermore, due to complexities in the password page, it was found that ABSA will function correctly even if the B password is submitted to the site (one character per textbox, that is). Hence, THIS version of the module actually submits your B password, and not only the requested characters. This does not have any adverse effect on the website, but has B security issues attached to it. B use carefully or rather wait for the next version of this module which will hopefully fix this issue -- any help will also be appreciated! =head1 WARNING This warning is from Simon Cozens' C, and seems just as apt here. This is code for B, and that means B, and that means B. You are encouraged, nay, expected, to audit the source of this module yourself to reassure yourself that I am not doing anything untoward with your banking data. This software is useful to me, but is provided under B, explicit or implied. =head1 THANKS Chris Ball for C, upon which a lot of this code is based. Also to Simon Cozens for C, upon which most of C is based, Andy Lester (and Skud, by continuation) for WWW::Mechanize, Gisle Aas for HTML::TokeParser. =head1 AUTHOR Leon Cowle C =cut