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