File Coverage

blib/lib/Finance/Bank/IE/PermanentTSB.pm
Criterion Covered Total %
statement 31 277 11.1
branch 1 138 0.7
condition 1 30 3.3
subroutine 11 17 64.7
pod 4 7 57.1
total 48 469 10.2


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Finance::Bank::IE::PermanentTSB - Perl Interface to the PermanentTSB
4             Open24 homebanking on L
5              
6             =head1 DESCRIPTION
7              
8             This is a set of functions that can be used in your Perl code to perform
9             some operations with a Permanent TSB homebanking account.
10              
11             Features:
12              
13             =over
14              
15             =item * B: retrieves the balance for all the accounts
16             you have set up (current account, visa card, etc.)
17              
18             =item * B: retrieves the
19             statement for a particular account, in a range of date.
20              
21             =item * B (to be implemented): top up your mobile
22             phone!
23              
24             =item * B (to be implemented): transfer money between your
25             accounts or third party accounts.
26              
27             =back
28              
29             =cut
30              
31             package Finance::Bank::IE::PermanentTSB;
32              
33             our $VERSION = '0.4';
34              
35 1     1   40642 use strict;
  1         2  
  1         39  
36 1     1   5 use warnings;
  1         1  
  1         28  
37 1     1   16 use Data::Dumper;
  1         10  
  1         53  
38 1     1   1123 use WWW::Mechanize;
  1         273796  
  1         40  
39 1     1   12 use HTML::TokeParser;
  1         3  
  1         23  
40 1     1   4 use Carp qw(croak carp);
  1         1  
  1         73  
41 1     1   908 use Date::Calc qw(check_date Delta_Days);
  1         39800  
  1         131  
42              
43 1     1   14 use base 'Exporter';
  1         2  
  1         213  
44             # export by default the check_balance function and the constants
45             our @EXPORT = qw(check_balance ALL WITHDRAWAL DEPOSIT VISA_ACCOUNT SWITCH_ACCOUNT);
46             our @EXPORT_OK = qw(mobile_topup account_statement);
47              
48             my %cached_cfg;
49             my $agent;
50             my $lastop = 0;
51              
52             my $BASEURL = "https://www.open24.ie/";
53              
54             my $error = 0;
55              
56             =head1 CONSTANTS
57              
58             The constants below are used with the account_statement() function:
59              
60             =over
61              
62             =item * C: shortcut for (WITHDRAWAL and DEPOSIT);
63              
64             =item * C: shows only the WITHDRAWALs;
65              
66             =item * C: shows only the DEPOSITs;
67              
68             =item * C: the account refers to a Visa Card;
69              
70             =item * C: the account is a normal Current Account;
71              
72             =back
73              
74             =cut
75              
76             # constant to be used with the account_statement() function
77             use constant {
78              
79             # statement types
80 1         3478 ALL => 0, # prints all the transactions
81             # (WITHDRAWAL and DEPOSIT)
82             WITHDRAWAL => 1, # shows only the WITHDRAWALs
83             DEPOSIT => 2, # shows only the DEPOSITs
84              
85             # account types
86             VISA_ACCOUNT => 'Visa Card', # visa card account
87             SWITCH_ACCOUNT => 'Switch Current A/C', # switch current account
88              
89 1     1   6 };
  1         2  
90              
91             =head1 METHODS / FUNCTIONS
92              
93             Every function in this module requires, as the first argument, a reference
94             to an hash which contains the configuration:
95              
96             my %config = (
97             "open24numba" => "your open24 number",
98             "password" => "your internet password",
99             "pan" => "your personal access number",
100             "debug" => 1,
101             );
102              
103             =head2 C<$boolean = login($config_ref)> - B
104              
105             B
106             You don't need to call it directly from you code!>
107              
108             This function performs the login. It takes just one required argument,
109             which is an hash reference for the configuration.
110             The function returns true (1) if success or undef for any other
111             state.
112             If debug => 1 then it will dump the html page on the current working
113             directory.
114             Please be aware that this has a security risk. The information will
115             persist on your filesystem until you reboot your machine (and /var/tmp
116             get clean at boot time).
117              
118             =cut
119              
120             sub login {
121 0     0 1 0 my $self = shift;
122 0         0 my $config_ref = shift;
123 0         0 my $content;
124              
125 0   0     0 $config_ref ||= \%cached_cfg;
126              
127 0   0     0 my $croak = ($config_ref->{croak} || 1);
128              
129 0         0 for my $reqfield ("open24numba", "password", "pan") {
130 0 0       0 if (! defined( $config_ref->{$reqfield})) {
131 0 0       0 if ($croak) {
132 0         0 carp("$reqfield not there!");
133 0         0 return undef;
134             } else {
135 0         0 carp("$reqfield not there!");
136 0         0 return undef;
137             }
138             }
139             }
140              
141 0 0       0 if(!defined($agent)) {
142 0         0 $agent = WWW::Mechanize->new( env_proxy => 1, autocheck => 1,
143             keep_alive => 10);
144 0         0 $agent->env_proxy;
145 0         0 $agent->quiet(0);
146 0         0 $agent->agent('Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.12) Gecko/20071126 Fedora/1.5.0.12-7.fc6 Firefox/1.5.0.12' );
147 0         0 my $jar = $agent->cookie_jar();
148 0         0 $jar->{hide_cookie2} = 1;
149 0         0 $agent->add_header('Accept' =>
150             'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5');
151 0         0 $agent->add_header('Accept-Language' => 'en-US,en;q=0.5');
152 0         0 $agent->add_header( 'Accept-Charset' =>
153             'ISO-8859-1,utf-8;q=0.7,*;q=0.7' );
154 0         0 $agent->add_header( 'Accept-Encoding' => 'gzip,deflate' );
155             } else {
156             # simple check to see if the login is live
157             # this based on Waider Finance::Bank::IE::BankOfIreland.pm!
158 0 0       0 if ( time - $lastop < 60 ) {
159 0 0       0 carp "Last operation 60 seconds ago, reusing old session"
160             if $config_ref->{debug};
161 0         0 $lastop = time;
162 0         0 return 1;
163             }
164 0         0 my $res = $agent->get( $BASEURL . '/online/Account.aspx' );
165 0 0       0 if ( $res->is_success ) {
166 0         0 $content = $agent->content;
167 0 0       0 if($agent->content =~ /ACCOUNT SUMMARY/is) {
168 0         0 $lastop = time;
169 0 0       0 carp "Short-circuit: session still valid"
170             if $config_ref->{debug};
171 0         0 return 1;
172             }
173             }
174 0 0       0 carp "Session has timed out, redoing login"
175             if $config_ref->{debug};
176             }
177              
178             # retrieve the login page
179 0         0 my $res = $agent->get($BASEURL . '/online/login.aspx');
180 0 0       0 $agent->save_content('./loginpage.html') if $config_ref->{debug};
181              
182             # something wrong?
183 0 0       0 if(!$res->is_success) {
184 0         0 carp("Unable to get page!");
185 0         0 return undef;
186             }
187              
188             # 2nd agent->content call
189 0         0 $content = $agent->content;
190 0 0       0 if($content =~ /Interruption Page/is) {
191 0         0 carp "Cannot authenticate on Open24.ie: site mantainance";
192 0         0 $error = 1;
193 0         0 return undef;
194             }
195              
196             # page not found?
197 0 0       0 if($content =~ /Page Not Found/is) {
198 0         0 carp("HTTP ERROR 404: Page Not Found");
199 0         0 return undef;
200             }
201              
202             # Login - Step 1 of 2
203 0         0 $agent->field('txtLogin', $config_ref->{open24numba});
204 0         0 $agent->field('txtPassword', $config_ref->{password});
205             # PermanentTSB website sucks...
206             # there's no normal submit button, the "continue" button is a
207             # link
208             # that launches a Javascript function. This function sets
209             # the __EVENTTARGET to 'lbtnContinue'. Here we are simulating this
210             # bypassing the Javascript code :)
211 0         0 $agent->field('__EVENTTARGET', 'lbtnContinue');
212 0         0 $res = $agent->submit();
213             # something wrong?
214 0 0       0 if(!$res->is_success) {
215 0         0 carp("Unable to get page!");
216 0         0 return undef;
217             }
218 0 0       0 $agent->save_content("./step1_result.html") if $config_ref->{debug};
219              
220             # 3rd agent->content call
221 0         0 $content = $agent->content;
222             # Login - Step 2 of 2
223 0 0       0 if($content !~ /LOGIN STEP 2 OF 2/is) {
224 0         0 carp("Problem 1 while authenticating!\nPlease don't retry ".
225             "this 3 times in a row or you account will be locked!");
226 0         0 return undef;
227             } else {
228 0 0       0 if($content !~ /txtDigit/is) {
229 0         0 carp("Problem 2 while authenticating!\nPlease don't retry ".
230             "this 3 times in a row or you account will be locked!");
231 0         0 return undef;
232             }
233 0 0       0 print $content if($config_ref->{debug});
234 0         0 set_pan_fields($agent, $config_ref);
235 0         0 $res = $agent->submit();
236 0 0       0 $agent->save_content("./step2_pan_result.html")
237             if $config_ref->{debug};
238 0 0       0 print $content if($config_ref->{debug});
239             }
240              
241 0         0 return 1;
242            
243             }
244              
245             =head2 C - B
246              
247             B
248             You don't need to call it directly from you code!>
249              
250             This is used for the second step of the login process.
251             The web interface ask you to insert 3 of the 6 digits that form the PAN
252             code.
253             The PAN is a secret code that only the PermanentTSB customer knows.
254             If your PAN code is 123234 and the web interface is asking for this:
255              
256             =over
257              
258             =item Digit no. 2:
259              
260             =item Digit no. 5:
261              
262             =item Digit no. 6:
263              
264             =back
265              
266             The function will fill out the form providing 2,3,4 respectively.
267              
268             This function doesn't return anything.
269              
270             =cut
271              
272             sub set_pan_fields {
273              
274 0     0 1 0 my $agent = shift;
275 0         0 my $config_ref = shift;
276              
277             # 4th agent->content call
278 0         0 my $p = HTML::TokeParser->new(\$agent->content());
279             # convert the pan string into an array
280 0         0 my @pan_digits = ();
281 0         0 my @pan_arr = split('',$config_ref->{pan});
282             # look for with ids "lblDigit1", "lblDigit2" and "lblDigit3"
283             # and build an array
284             # the PAN, Personal Access Number is formed by 6 digits.
285 0         0 while (my $tok = $p->get_tag("span")){
286 0 0       0 if(defined $tok->[1]{id}) {
287 0 0       0 if($tok->[1]{id} =~ m/lblDigit[123]/) {
288 0         0 my $text = $p->get_trimmed_text("/span");
289             # normally the webpage shows Digit No. x
290             # where x is the position of the digit inside
291             # the PAN number assigne by the bank to the owner of the
292             # account
293             # here we are building the @pan_digits array
294 0         0 push @pan_digits, $pan_arr[substr($text,10)-1];
295             }
296             }
297             }
298 0         0 $agent->field('txtDigitA', $pan_digits[0]);
299 0         0 $agent->field('txtDigitB', $pan_digits[1]);
300 0         0 $agent->field('txtDigitC', $pan_digits[2]);
301 0         0 $agent->field('__EVENTTARGET', 'btnContinue');
302             }
303              
304             =head2 C<@accounts_balance = check_balance($config_ref)> - B
305              
306             This function require the configuration hash reference as argument.
307             It returns an reference to an array of hashes, one hash for each account.
308             In case of error it return undef;
309             Each hash has these keys:
310              
311             =over
312              
313             =item * 'accname': account name, i.e. "Switch Current A/C".
314              
315             =item * 'accno': account number. An integer representing the last 4 digits of the
316             account.
317              
318             =item * 'accbal': account balance. In EURO.
319              
320             =back
321              
322             Here is an example:
323              
324             $VAR1 = {
325             'availbal' => 'euro amount',
326             'accno' => '0223',
327             'accbal' => 'euro amount',
328             'accname' => 'Switch Current A/C'
329             };
330             $VAR2 = {
331             'availbal' => 'euro amount',
332             'accno' => '2337',
333             'accbal' => 'euro amount',
334             'accname' => 'Visa Card'
335             };
336              
337             The array can be printed using, for example, a foreach loop like this
338             one:
339              
340             foreach my $acc (@$balance) {
341             printf ("%s ending with %s: %s\n",
342             $acc->{'accname'},
343             $acc->{'accno'},
344             $acc->{'accbal'}
345             );
346             }
347              
348             =cut
349              
350             sub check_balance {
351              
352 0     0 1 0 my $self = shift;
353 0         0 my $config_ref = shift;
354 0         0 my $res;
355              
356 0   0     0 $config_ref ||= \%cached_cfg;
357 0   0     0 my $croak = ($config_ref->{croak} || 1);
358            
359 0 0       0 $self->login($config_ref) or return undef;
360              
361 0         0 my $p = HTML::TokeParser->new(\$agent->content());
362 0         0 my $i = 0;
363 0         0 my @array;
364 0         0 my $hash_ref = {};
365 0         0 while (my $tok = $p->get_tag("td")){
366 0 0       0 if(defined $tok->[1]{style}) {
367 0 0       0 if($tok->[1]{style} eq 'width:25%;') {
368 0         0 my $text = $p->get_trimmed_text("/td");
369 0 0       0 if($i == 0) {
370 0         0 $hash_ref = {};
371 0         0 $hash_ref->{'accname'} = $text;
372             }
373 0 0       0 if($i == 1) {
374 0         0 $hash_ref->{'accno'} = $text;
375             }
376 0 0       0 if($i == 2) {
377 0         0 $hash_ref->{'accbal'} = $text;
378             }
379 0 0       0 if($i == 3) {
380 0         0 $hash_ref->{'availbal'} = $text;
381             }
382 0         0 $i++;
383 0 0       0 if($i == 4) {
384 0         0 $i = 0;
385 0         0 push @array, $hash_ref;
386             }
387             }
388             }
389             }
390              
391 0         0 return \@array;
392              
393             }
394              
395             =head2 C<@account_statement = account_statement($config_ref, $acc_type,
396             $acc_no, $from, $to, [$type])> - B
397              
398             This function requires 4 mandatory arguments, the 5th is optional.
399              
400             =over
401              
402             =item 1. B<$config_ref>: the hash reference to the configuration
403              
404             =item 2. B<$acc_type>: this is a constant: can be VISA_ACCOUNT or SWITCH_ACCOUNT
405              
406             =item 3. B<$acc_no>: this is a 4 digits field representing the last 4
407             digits of the account number (or Visa card number)
408              
409             =item 4. B<$from>: from date, in format dd/mm/yyyy
410              
411             =item 5. B<$to>: to date, in format dd/mm/yyyy
412              
413             =item 6. B<$type> (optional): type of statement (optional). Default: ALL.
414             It can be WITHDRAWAL, DEPOSIT or ALL.
415              
416             =back
417              
418             The function returns an reference to an array of hashes, one hash for each row of the statement.
419             The array of hashes can be printed using, for example, a foreach loop like
420             this one:
421              
422             foreach my $row (@$statement) {
423             printf("%s | %s | %s | %s \n",
424             $row->{date},
425             $row->{description},
426             $row->{euro_amount},
427             $row->{balance});
428             }
429              
430             Undef is returned in case of error;
431              
432             =cut
433              
434             sub account_statement {
435            
436 0     0 1 0 my ($self, $config_ref, $acc_type, $acc_no, $from, $to, $type) = @_;
437 0         0 my ($res, @ret_array);
438              
439 0   0     0 $config_ref ||= \%cached_cfg;
440 0   0     0 my $croak = ($config_ref->{croak} || 1);
441              
442 0 0       0 if(defined $acc_type) {
443 0 0 0     0 if($acc_type ne SWITCH_ACCOUNT and $acc_type ne VISA_ACCOUNT) {
444 0         0 carp("Account type is invalid");
445 0         0 return undef;
446             }
447             } else {
448 0         0 carp("Account type not defined");
449 0         0 return undef;
450             }
451              
452 0 0       0 if(not defined $acc_no) {
453 0         0 carp("Account number not defined.");
454 0         0 return undef;
455             }
456              
457 0         0 my $account = $acc_type." - ".$acc_no;
458              
459 0 0 0     0 if(defined $from and defined $to) {
460              
461             # $from should be > of $to
462 0         0 my @d_from = split "/", $from;
463 0         0 my @d_to = split "/", $to;
464 0 0       0 if (Delta_Days($d_from[0],$d_from[1],$d_from[2],
465             $d_to[0],$d_to[1],$d_to[2]) <= 0) {
466              
467 0         0 carp("Date range $from -> $to invalid.");
468 0         0 return undef;
469              
470             }
471              
472             # check date_from, date_to
473 0         0 foreach my $date ($from, $to) {
474             # date should be in format yyyy/mm/dd
475 0 0       0 if(not $date =~ /^\d{4}\/\d{2}\/\d{2}$/) {
476 0         0 carp("Date $date should be in format 'yyyy/mm/dd'");
477 0         0 return undef;
478             }
479             # date should be valid, this is using Date::Calc->check_date()
480 0         0 my @d = split "/", $date;
481 0 0       0 if (not check_date($d[0],$d[1],$d[2])) {
482 0         0 carp("Date $date is not valid!");
483 0         0 return undef;
484             }
485             }
486             } else {
487 0         0 carp("Date range not defined");
488 0         0 return undef;
489             }
490              
491 0 0       0 if(defined $account) {
492 0 0       0 if(not $account =~ m/.+ - \d{4}$/) {
493 0         0 carp("$account is invalid");
494 0         0 return undef;
495             }
496             }
497              
498             # verify if the account exists inside the homebanking
499 0         0 my $acc = $self->check_balance($config_ref);
500 0 0       0 if(not defined $acc) { return undef; }
  0         0  
501 0         0 my $found = 0;
502 0         0 foreach my $c (@$acc) {
503 0 0       0 if($account eq $c->{'accname'}." - ".$c->{'accno'}) {
504 0         0 $found = 1;
505 0         0 last;
506             }
507             }
508              
509 0 0       0 if($found) {
510              
511 0 0       0 $self->login($config_ref) or return undef;
512              
513             # go to the Statement page
514 0         0 $res = $agent->get($BASEURL . '/online/Statement.aspx');
515 0 0       0 $agent->save_content("./statement_page.html")
516             if $config_ref->{debug};
517              
518 0         0 $agent->field('ddlAccountName', $account);
519 0         0 $agent->field('__EVENTTARGET', 'lbtnShow');
520 0         0 $res = $agent->submit();
521             # something wrong?
522 0 0       0 if(!$res->is_success) {
523 0         0 carp("Unable to get page!");
524 0         0 return undef;
525             }
526 0 0       0 $agent->save_content("./statement_page2.html")
527             if $config_ref->{debug};
528              
529             # fill out the "from" date
530 0         0 my @d = split "/", $from;
531 0         0 $agent->field('ddlFromDay', $d[2]);
532 0         0 $agent->field('ddlFromMonth', $d[1]);
533 0         0 $agent->field('ddlFromYear', $d[0]);
534              
535             # fill out the "to" date
536 0         0 @d = split "/", $to;
537 0         0 $agent->field('ddlToDay', $d[2]);
538 0         0 $agent->field('ddlToMonth', $d[1]);
539 0         0 $agent->field('ddlToYear', $d[0]);
540              
541 0 0       0 if(defined $type) {
542 0 0       0 $agent->field('grpTransType', 'rbWithdrawal')
543             if($type == WITHDRAWAL);
544 0 0       0 $agent->field('grpTransType', 'rbDeposit')
545             if($type == DEPOSIT);
546             }
547              
548 0         0 $agent->field('__EVENTTARGET', 'lbtnShow');
549 0         0 $res = $agent->submit();
550             # something wrong?
551 0 0       0 if(!$res->is_success) {
552 0         0 carp("Unable to get page!");
553 0         0 return undef;
554             }
555 0 0       0 $agent->save_content("./statement_page1.html")
556             if $config_ref->{debug};
557              
558 0         0 my $content = $agent->content;
559             # PermanentTSB doesn't support statements that include data
560             # older than 6 months... in this case the interface will reset
561             # to the default date range. We just need to print an warning
562             # and submit the current form as is
563 0 0       0 if($content =~ /YOU HAVE REQUESTED DATA OLDER THAN 6 MONTHS/is) {
564              
565 0         0 carp("PermanentTSB doesn't support queries older than 6".
566             " months! Resetting to the default date.");
567 0         0 $agent->field('__EVENTTARGET', 'lbtnShow');
568 0         0 $res = $agent->submit();
569 0 0       0 if(!$res->is_success) {
570 0         0 carp("Unable to get page!");
571 0         0 return undef;
572             }
573 0 0       0 $agent->save_content("./statement_res_after_6months.html")
574             if $config_ref->{debug};
575             }
576              
577 0 0       0 if($content =~ /INCORRECT DATE CRITERIA ENTERED: 'TO DATE' WAS IN THE FUTURE./is) {
578              
579 0         0 carp("Incorrect date criteria entered: 'to date' was in the".
580             " future! Resetting to the default date. ");
581             }
582            
583             # parse output page clicking "next" button until the
584             # button "another statement" is present. all the data must
585             # be inserted into an array of hashes.
586             # the array should contain an hash per row.
587             # every hash contains [date, description, euro_amount, balance]
588 0         0 my $hash_ref = {};
589 0         0 my $visa = 0;
590 0         0 my $page = 1;
591 0         0 my $i = 1;
592 0         0 while (1) {
593 0         0 my $p = HTML::TokeParser->new(\$agent->content());
594 0         0 while (my $tok = $p->get_tag('table')) {
595 0 0       0 if(defined $tok->[1]{id}) {
596 0 0       0 if($tok->[1]{id} eq 'tblTransactions'){
597 0         0 while(my $tok2 = $p->get_tag('tr')) {
598 0         0 $hash_ref = {};
599 0         0 my $text = $p->get_trimmed_text('/tr');
600             #TODO: improve regexp!
601             # this matches the html row
602             # dd/mm/yyyy description [-/+] amount balance [-/+]
603             # example -> 29/09/2008 DUNNES STEPHEN 29/09 - 45.00 25000.00 +
604 0 0       0 if($text =~ /^(\d{2}\/\d{2}\/\d{4}) (.+) ([-\+] [\d\.]+) ([\d\.]+ [-\+])$/) {
605             # this is a normal current account
606             # statement
607 0         0 $hash_ref->{date} = $1;
608 0         0 $hash_ref->{description} = $2;
609 0         0 $hash_ref->{euro_amount} = $3;
610 0         0 $hash_ref->{balance} = $4;
611 0         0 $hash_ref->{euro_amount} =~ s/\s//g;
612 0 0       0 if($hash_ref->{balance} =~ /^([\d\.]+) ([-\+])$/) {
613 0 0       0 if($2 eq '+') {
614 0         0 $hash_ref->{balance} = "+".$1;
615             }
616 0 0       0 if($2 eq '-') {
617 0         0 $hash_ref->{balance} = "-".$1;
618             }
619             }
620 0 0 0     0 push @ret_array, $hash_ref
      0        
      0        
621             if(($i==1 and $page == 1) or ($i>1 and $page>=1));
622 0         0 $i++;
623             }
624 0 0       0 if($text =~ /^(\d{2}\/\d{2}\/\d{4}) (\d{2}\/\d{2}\/\d{4}) (.+) ([-\+] [\d\.]+)$/) {
625             # this is a visa card statement
626 0         0 $visa = 1;
627 0         0 $hash_ref->{date} = $1;
628 0         0 $hash_ref->{description} = $3;
629 0         0 $hash_ref->{euro_amount} = $4;
630 0         0 $hash_ref->{euro_amount} =~ s/\s//g;
631 0         0 push @ret_array, $hash_ref;
632             # if(($i==1 and $page == 1) or ($i>1 and $page>=1));
633 0         0 $i++;
634             }
635             }
636             }
637             }
638             }
639             # if we are at the last page we will find a button called
640             # Another Statement, exit from the while loop
641 0 0       0 if($agent->content =~ /Another Statement/is) {
642 0         0 last;
643             } else {
644             # the "next" buttons have different target names
645             # it depdends if we are watching a visa card or a normal
646             # current account
647 0 0       0 if($visa) {
648 0         0 $agent->field('__EVENTTARGET', 'lBtnAnother1');
649             } else {
650 0         0 $agent->field('__EVENTTARGET', 'lbtnShow');
651             }
652 0         0 $page++;
653 0         0 $i = 1;
654             }
655 0         0 $res = $agent->submit();
656 0 0       0 $agent->save_content("./statement_page".($page-1).".html")
657             if $config_ref->{debug};
658             # something wrong?
659 0 0       0 if(!$res->is_success) {
660 0         0 carp("Unable to get page!");
661 0         0 return undef;
662             }
663             }
664              
665             } else {
666              
667             # account doesn't exist in the homebanking interface
668             # return undef
669 0         0 carp("Account $account not found!");
670 0         0 return undef;
671              
672             }
673              
674 0         0 return \@ret_array;
675              
676             }
677              
678             # TODO: implement this
679 0     0 0 0 sub funds_transfer {
680              
681             }
682              
683             # TODO: implement this
684 0     0 0 0 sub mobile_topup {
685              
686             }
687              
688             sub logoff {
689 1     1 0 3 my $self = shift;
690 1         2 my $config_ref = shift;
691              
692 1 50 33     13 if(defined $agent and not $error) {
693              
694 0           my $res = $agent->get($BASEURL . '/online/DoLogOff.aspx');
695 0 0         $agent->save_content("./logoff.html") if $config_ref->{debug};
696 0           $agent->field('__EVENTTARGET', 'lbtnContinue');
697 0           $agent->submit;
698             }
699             }
700              
701             END {
702 1     1   5 logoff;
703             }
704              
705             1;
706              
707             __END__