File Coverage

blib/lib/Finance/Bank/AU/StGeorge.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Finance::Bank::AU::StGeorge;
2              
3 1     1   6459 use 5.005;
  1         5  
  1         43  
4 1     1   1023 use HTTP::Request::Common qw/POST/;
  1         65830  
  1         89  
5 1     1   7520 use WWW::Mechanize;
  0            
  0            
6             use strict;
7              
8             use vars qw($VERSION $IBANK_URL $ERROR);
9              
10             $VERSION = '0.01';
11             $IBANK_URL = "https://ibank.stgeorge.com.au/scripts/ibank.dll?ibank";
12             $ERROR = '';
13              
14             sub new
15             {
16             my ($class, %args) = @_;
17              
18             my $self = bless {
19             _ua => WWW::Mechanize->new(autocheck => 1),
20             card => "",
21             pin => "",
22             pass => "",
23             issue => 1,
24             }, $class;
25              
26             return $self->_set(%args);
27             }
28              
29             sub _set
30             {
31             my ($self, %args) = @_;
32              
33             $self->{lc $_} = $args{$_}
34             foreach grep !/^_/ && exists $self->{lc $_}, keys %args;
35              
36             return $self;
37             }
38              
39             sub logged_in
40             {
41             my ($self) = @_;
42             return unless $self->{_params};
43             return unless $self->{_params}{Session};
44             }
45              
46             sub login
47             {
48             my ($self, %args) = @_;
49              
50             $self = $self->new(%args) unless ref $self;
51              
52             my $params = $self->{_params} ||= {
53             Route => "IBS",
54             Id => "JBANK.12.P",
55             origin => "ABA",
56             Session => "",
57             };
58              
59             return $self if $params->{Session};
60              
61             my $ua = $self->{_ua};
62             $ua->env_proxy;
63             $ua->get("https://ibank.stgeorge.com.au/html/index.asp");
64              
65             my ($popup_url) = $ua->content =~ m|window\.open\(\"([^\"]+)\"|;
66              
67             $ua->get($popup_url);
68              
69             # one-time warnings
70             $ua->form(1);
71             $ua->submit();
72              
73             # login
74             my $form = $ua->form(1);
75             $form->value(Card => $self->{card});
76             $form->value(Pin => $self->{pin});
77             $form->value(LastName => $self->{pass});
78             $form->value(hWidth => 800);
79             $form->value(hHeight => 600);
80             $form->value(Issue => $self->{issue});
81             $ua->click();
82              
83             # scrape out session id's and stuff
84             for ($ua->content)
85             {
86             ($params->{Route}) = $1 if /route=([^\&]+)/m;
87             ($params->{Id}) = $1 if /clid=([^\&]+)/m;
88             ($params->{origin}) = $1 if /origin=([^\&]+)/m;
89             ($params->{Session}) = $1 if /Session=([a-f0-9]{32})/m;
90             }
91              
92             return $self if $params->{Session};
93             return;
94             }
95              
96             sub logout
97             {
98             my ($self) = @_;
99              
100             $self->logged_in or return;
101              
102             my $ua = $self->{_ua};
103              
104             $ua->request(POST $IBANK_URL, [
105             route => "IBS",
106             params => _format_params(%{ $self->{_params} }, Tran => "Logout"),
107             ]);
108              
109             return 1;
110             }
111              
112             sub accounts
113             {
114             my ($self, %args) = @_;
115              
116             $self = $self->new(%args) unless ref $self;
117             $self->login or return;
118              
119             my $ua = $self->{_ua};
120              
121             my $accounts = $self->{_accounts};
122              
123             if (not $accounts or $args{reload})
124             {
125             $ua->request(POST $IBANK_URL, [
126             route => "IBS",
127             params => _format_params(%{ $self->{_params} }, Tran => "BrowseAccounts"),
128             ]);
129              
130             $accounts = $self->{_accounts} = [ _parse_params($ua->content) ];
131             }
132              
133             my @ret;
134              
135             foreach (@$accounts)
136             {
137             next unless ref $_;
138             next unless ($args{type} ||= "ACC") eq "ALL" or $args{type} eq $_->{Type};
139              
140             $_->{_parent} = $self;
141              
142             if ($_->{Type} eq "ACC")
143             {
144             push @ret, bless $_, "Finance::Bank::AU::StGeorge::Account";
145             }
146             elsif ($_->{Type} eq "ThirdParty")
147             {
148             push @ret, bless $_, "Finance::Bank::AU::StGeorge::ForeignAccount";
149             }
150             }
151              
152             return wantarray ? @ret : $ret[0];
153             }
154              
155             sub _account_detail
156             {
157             my ($self, $acc, %args) = @_;
158              
159             my $ua = $self->{_ua};
160              
161             $ua->request(POST $IBANK_URL, [
162             route => "IBS",
163             params => _format_params(%{ $self->{_params} },
164             Tran => "BrowseDetail",
165             Type => "ACC", # ALL w/o Account and AccountCode
166             Account => $acc->number,
167             AccountCode => $acc->code,
168             RequestFlag => "BCT",
169             ),
170             ]);
171              
172             return _parse_params($ua->content);
173             }
174              
175             sub _account_history_csv
176             {
177             my ($self, $acc, %args) = @_;
178              
179             my $ua = $self->{_ua};
180              
181             $ua->request(POST $IBANK_URL, [
182             route => "IBS",
183             params => _format_params(%{ $self->{_params} },
184             Tran => "ExportAccountHistory",
185             Type => "ACC",
186             Account => $acc->number,
187             AccountCode => $acc->code,
188             Format => "CSV",
189             $args{start} ? (FromDate => $args{start}) : (), # 20050123
190             $args{end} ? (ToDate => $args{end}) : (), # 20050123
191             DateFormat => "%d/%m/%Y",
192             ),
193             ]);
194              
195             my @ret;
196             my @fields;
197              
198             foreach (split /\r?\n/, (_parse_params($ua->content))[0])
199             {
200             if (@fields)
201             {
202             my %ret;
203             @ret{ @fields } = split /,/, $_;
204             push @ret, bless \%ret, "Finance::Bank::AU::StGeorge::History";
205             }
206             else
207             {
208             @fields = split /,/, $_;
209             }
210             }
211              
212             return @ret;
213             }
214              
215             sub _transfer
216             {
217             my ($self, $from, $to, %args) = @_;
218              
219             unless ($from->type eq "ACC")
220             {
221             die "Can only tranfer from a local account: ".$from->type."\n";
222             }
223              
224             unless ($args{amount} =~ /^\d+\.\d\d$/)
225             {
226             die "You must specify a valid amount to transfer\n";
227             }
228              
229             unless ($to->type eq "ACC" or $args{payer})
230             {
231             die "You must specify a payer name to third party transfers\n";
232             }
233              
234             my $ua = $self->{_ua};
235              
236             $ua->request(POST $IBANK_URL, [
237             route => "IBS",
238             params => _format_params(%{ $self->{_params} },
239             Tran => "Payment",
240             Mode => "C",
241             Frequency => "now",
242             NotifyByEmail => "false",
243             Type => $from->type,
244             Account => $from->account,
245             AccountCode => $from->code,
246             )._format_params(
247             ToType => $to->type,
248             $to->type eq "ACC" ? (
249             ToAccount => $to->account,
250             ToAccountCode => $to->code
251             ) : (
252             ToAccount => $to->account,
253             ),
254             Amount => $args{amount},
255             $to->type ne "ACC" ? (
256             Payer => $args{payer},
257             ) : (),
258             Reference => $args{reference} || "Funding Terrorism",
259             ),
260             ]);
261              
262             my @ret = _parse_params($ua->content);
263              
264             if (@ret == 1 and $ret[0]->{Receipt})
265             {
266             # adjust balances on local accounts if available
267             $from->{AvailBalance} = $ret[0]->{FromAvailBalance}
268             if length $ret[0]->{FromAvailBalance};
269             $from->{Balance} = $ret[0]->{FromBalance}
270             if length $ret[0]->{FromBalance};
271             $to->{AvailBalance} = $ret[0]->{ToAvailBalance}
272             if length $ret[0]->{ToAvailBalance};
273             $to->{Balance} = $ret[0]->{ToBalance}
274             if length $ret[0]->{ToBalance};
275              
276             return bless $ret[0], "Finance::Bank::AU::StGeorge::Receipt";
277             }
278              
279             return;
280             }
281              
282             # sub _account_history
283             # {
284             # my ($self, $acc, %args) = @_;
285             #
286             # my $ua = $self->{_ua};
287             #
288             # $ua->request(POST $IBANK_URL, [
289             # route => "IBS",
290             # params => _format_params(
291             # %{ $self->{_params} },
292             # Tran => "BrowseDetail",
293             # Type => "ACC",
294             # Account => $acc->number,
295             # AccountCode => $acc->code,
296             # RequestFlag => "H",
297             # $args{start} ? (FromDate => $args{start}) : (), # 20050123 / -30
298             # $args{end} ? (ToDate => $args{end}) : (), # 20050123
299             # ),
300             # ]);
301             #
302             # return _parse_params($ua->content);
303             # }
304              
305             sub DESTROY
306             {
307             shift->logout;
308             }
309              
310             sub _format_params
311             {
312             my $ret;
313              
314             while (my ($k, $v) = splice(@_, 0, 2))
315             {
316             $ret .= join(chr(0x1c), $k, $v).chr(0x1d);
317             }
318              
319             $ret .= chr(0x1e);
320              
321             return $ret;
322             }
323              
324             sub _parse_params
325             {
326             my @ret;
327              
328             foreach my $r (split /\x1e/, $_[0])
329             {
330             my $p = {};
331              
332             foreach my $g (split /\x1d/, $r)
333             {
334             my ($k, @v) = split /\x1c/, $g, -1;
335              
336             if (@v > 1 or exists $p->{$k})
337             {
338             unshift @v, delete $p->{$k}
339             if exists $p->{$k};
340              
341             push @{ $p->{$k} }, @v;
342             }
343             elsif (@v)
344             {
345             $p->{$k} = $v[0];
346             }
347             else
348             {
349             $p = $k;
350             }
351             }
352              
353             push @ret, $p;
354             }
355              
356             unless (shift(@ret) =~ /^OK\w+$/)
357             {
358             $ERROR = $ret[0]->{Message} if @ret;
359             $ERROR ||= "Non-OK Response";
360             return;
361             }
362              
363             return @ret;
364             }
365              
366             package Finance::Bank::AU::StGeorge::Account;
367              
368             # 'Icon' => 'savings.gif',
369             # 'Flags' => 'WDHBIER',
370             # 'Account' => '0000000000000',
371             # 'AccountCode' => 'SAV',
372             # 'AccountTitle' => '',
373             # 'DEUser' => '',
374             # 'Number' => '0000000000000', # same as Account
375             # 'SubProdCode' => '0000',
376             # 'Type' => 'ACC',
377             # 'IsSegmented' => 'false',
378             # 'TypeName' => 'Savings',
379             # 'Balance' => '0.00',
380             # 'Bsb' => '',
381             # 'Name' => '',
382             # 'AvailBalance' => '0.00'
383              
384             sub type { $_[0]->{Type} }
385             sub code { $_[0]->{AccountCode} }
386             sub number { $_[0]->{Account} }
387             sub account { $_[0]->number }
388             sub name { $_[0]->{TypeName} }
389             sub balance { $_[0]->{Balance} }
390             sub available { $_[0]->{AvailBalance} }
391              
392             sub detail { ($_[0]->{_parent}->_account_detail(@_))[0] }
393             sub history { $_[0]->{_parent}->_account_history_csv(@_) }
394             sub transfer { $_[0]->{_parent}->_transfer(@_) }
395              
396             package Finance::Bank::AU::StGeorge::ForeignAccount;
397              
398             # 'InternetTP' => 'true',
399             # 'Icon' => 'ithirdparty.gif',
400             # 'Payee' => '000000-000000000', # Bsb-Account
401             # 'Account' => '000000000',
402             # 'Number' => '000000-000000000', # Bsb-Account
403             # 'Type' => 'ThirdParty',
404             # 'Bsb' => '000000',
405             # 'Name' => ''
406              
407             sub type { $_[0]->{Type} }
408             sub bsb { $_[0]->{Bsb} }
409             sub number { $_[0]->{Account} }
410             sub account { join("-", $_[0]->bsb, $_[0]->number) }
411             sub name { $_[0]->{Name} }
412              
413             package Finance::Bank::AU::StGeorge::History;
414              
415             # 'Debit' => '0.00', # empty string if credit
416             # 'Balance' => '0.00',
417             # 'Credit' => '0.00', # empty string if debit
418             # 'Description' => '',
419             # 'Date' => '24/01/2005'
420              
421             sub date { $_[0]->{Date} }
422             sub debit { $_[0]->{Debit} }
423             sub credit { $_[0]->{Credit} }
424             sub balance { $_[0]->{Balance} }
425             sub description { $_[0]->{Description} }
426              
427             package Finance::Bank::AU::StGeorge::Receipt;
428              
429             # 'Receipt' => '', # the big text field
430             # 'Message' => '',
431             # 'ToAvailBalance' => '0.00',
432             # 'ToBalance' => '0.00',
433             # 'FromAvailBalance' => '0.00',
434             # 'FromBalance' => '0.00',
435              
436             sub receipt { $_[0]->{Receipt} }
437              
438             1;
439             __END__