| blib/lib/Finance/Bank/Postbank_de.pm | |||
|---|---|---|---|
| Criterion | Covered | Total | % |
| statement | 79 | 96 | 82.2 |
| branch | 12 | 18 | 66.6 |
| condition | 6 | 9 | 66.6 |
| subroutine | 22 | 31 | 70.9 |
| pod | 8 | 18 | 44.4 |
| total | 127 | 172 | 73.8 |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | package Finance::Bank::Postbank_de; | ||||||
| 2 | |||||||
| 3 | 9 | 9 | 666271 | use 5.006; # we use lexical filehandles now | |||
| 9 | 67 | ||||||
| 4 | 9 | 9 | 62 | use strict; | |||
| 9 | 23 | ||||||
| 9 | 223 | ||||||
| 5 | 9 | 9 | 58 | use warnings; | |||
| 9 | 18 | ||||||
| 9 | 290 | ||||||
| 6 | 9 | 9 | 68 | use Carp; | |||
| 9 | 26 | ||||||
| 9 | 707 | ||||||
| 7 | 9 | 9 | 5002 | use Moo 2; | |||
| 9 | 103934 | ||||||
| 9 | 57 | ||||||
| 8 | |||||||
| 9 | 9 | 9 | 16516 | use Time::Local; | |||
| 9 | 15150 | ||||||
| 9 | 561 | ||||||
| 10 | 9 | 9 | 4529 | use POSIX 'strftime'; | |||
| 9 | 56829 | ||||||
| 9 | 46 | ||||||
| 11 | |||||||
| 12 | 9 | 9 | 17496 | use Finance::Bank::Postbank_de::Account; | |||
| 9 | 43 | ||||||
| 9 | 331 | ||||||
| 13 | 9 | 9 | 4303 | use Finance::Bank::Postbank_de::APIv1; | |||
| 9 | 40 | ||||||
| 9 | 451 | ||||||
| 14 | 9 | 9 | 75 | use Encode qw(decode); | |||
| 9 | 20 | ||||||
| 9 | 914 | ||||||
| 15 | 9 | 9 | 69 | use Mozilla::CA; | |||
| 9 | 22 | ||||||
| 9 | 2443 | ||||||
| 16 | |||||||
| 17 | #use IO::Socket::SSL qw(SSL_VERIFY_PEER SSL_VERIFY_NONE); | ||||||
| 18 | |||||||
| 19 | our $VERSION = '0.57'; | ||||||
| 20 | |||||||
| 21 | has 'login' => ( | ||||||
| 22 | is => 'ro', | ||||||
| 23 | ); | ||||||
| 24 | |||||||
| 25 | has 'password' => ( | ||||||
| 26 | is => 'ro', | ||||||
| 27 | ); | ||||||
| 28 | |||||||
| 29 | has 'urls' => ( | ||||||
| 30 | is => 'ro', | ||||||
| 31 | default => sub { {} }, | ||||||
| 32 | ); | ||||||
| 33 | |||||||
| 34 | has 'logger' => ( | ||||||
| 35 | is => 'ro', | ||||||
| 36 | default => sub { {} }, | ||||||
| 37 | ); | ||||||
| 38 | |||||||
| 39 | has 'past_days' => ( | ||||||
| 40 | is => 'ro', | ||||||
| 41 | default => sub { {} }, | ||||||
| 42 | ); | ||||||
| 43 | |||||||
| 44 | has 'api' => ( | ||||||
| 45 | is => 'rw', | ||||||
| 46 | default => sub { | ||||||
| 47 | my $api = Finance::Bank::Postbank_de::APIv1->new(); | ||||||
| 48 | $api->configure_ua(); | ||||||
| 49 | $api | ||||||
| 50 | }, | ||||||
| 51 | ); | ||||||
| 52 | |||||||
| 53 | has 'session' => ( | ||||||
| 54 | is => 'rw', | ||||||
| 55 | ); | ||||||
| 56 | |||||||
| 57 | has '_account_numbers' => ( | ||||||
| 58 | is => 'rw', | ||||||
| 59 | ); | ||||||
| 60 | |||||||
| 61 | our %functions; | ||||||
| 62 | BEGIN { | ||||||
| 63 | 9 | 9 | 10914 | %functions = ( | |||
| 64 | quit => [ text_regex => qr'\bBanking\s+beenden\b' ], | ||||||
| 65 | accountstatement => [ text_regex => qr'\bUms.*?tze\b' ], | ||||||
| 66 | ); | ||||||
| 67 | }; | ||||||
| 68 | |||||||
| 69 | around BUILDARGS => sub { | ||||||
| 70 | my ($orig,$class,%args) = @_; | ||||||
| 71 | |||||||
| 72 | croak "Login/Account number must be specified" | ||||||
| 73 | unless $args{login}; | ||||||
| 74 | croak "Password/PIN must be specified" | ||||||
| 75 | unless $args{password}; | ||||||
| 76 | if( exists $args{ status }) { | ||||||
| 77 | $args{ logger } = delete $args{ status }; | ||||||
| 78 | }; | ||||||
| 79 | |||||||
| 80 | $orig->($class, %args); | ||||||
| 81 | }; | ||||||
| 82 | |||||||
| 83 | 0 | 0 | 0 | 0 | sub log { $_[0]->logger->(@_); }; | ||
| 84 | 0 | 0 | 0 | 0 | sub log_httpresult { $_[0]->log("HTTP Code",$_[0]->agent->status,$_[0]->agent->res->headers->as_string . $_[0]->agent->content) }; | ||
| 85 | |||||||
| 86 | sub new_session { | ||||||
| 87 | 7 | 7 | 1 | 643 | my ($self) = @_; | ||
| 88 | |||||||
| 89 | 7 | 50 | 54 | $self->close_session() | |||
| 90 | if ($self->session); | ||||||
| 91 | 7 | 18 | my $pb; | ||||
| 92 | 7 | 17 | my $ok = eval { | ||||
| 93 | 7 | 65 | $pb = $self->api->login( $self->login, $self->password ); | ||||
| 94 | 4 | 7106 | 1 | ||||
| 95 | }; | ||||||
| 96 | 7 | 100 | 130 | if( ! $ok ) { | |||
| 97 | #warn sprintf "Got HTTP error %d, message %s", $self->api->ua->status, $self->api->ua->message; | ||||||
| 98 | #croak $@; | ||||||
| 99 | } else { | ||||||
| 100 | 4 | 40 | $self->session( $pb ); | ||||
| 101 | } | ||||||
| 102 | }; | ||||||
| 103 | |||||||
| 104 | sub is_security_advice { | ||||||
| 105 | 0 | 0 | 0 | 0 | my ($self) = @_; | ||
| 106 | #$self->agent->content() =~ /\bZum\s+Finanzstatus\b/; | ||||||
| 107 | }; | ||||||
| 108 | |||||||
| 109 | sub is_nutzungshinweis { | ||||||
| 110 | 0 | 0 | 0 | 0 | my ($self) = @_; | ||
| 111 | #$self->agent->content() =~ /\bAus Sicherheitsgr.*?nden haben wir einige\b/; | ||||||
| 112 | }; | ||||||
| 113 | |||||||
| 114 | |||||||
| 115 | sub skip_security_advice { | ||||||
| 116 | 0 | 0 | 0 | 0 | my ($self) = @_; | ||
| 117 | #$self->log('Skipping security advice page'); | ||||||
| 118 | #$self->agent->follow_link(text_regex => qr/\bZum\s+Finanzstatus\b/); | ||||||
| 119 | # $self->agent->content() =~ /Sicherheitshinweis/; | ||||||
| 120 | }; | ||||||
| 121 | |||||||
| 122 | sub skip_nutzungshinweis { | ||||||
| 123 | 0 | 0 | 0 | 0 | my ($self) = @_; | ||
| 124 | #$self->log('Skipping nutzungshinweis page'); | ||||||
| 125 | #$self->agent->follow_link(text_regex => qr/\bZur\s+Konten.bersicht\b/); | ||||||
| 126 | # $self->agent->content() =~ /Sicherheitshinweis/; | ||||||
| 127 | }; | ||||||
| 128 | |||||||
| 129 | sub error_page { | ||||||
| 130 | # Check if an error page is shown | ||||||
| 131 | 3 | 3 | 0 | 22 | my ($self) = @_; | ||
| 132 | 3 | 28 | $self->api->ua->status != 200 | ||||
| 133 | #return unless $self->agent; | ||||||
| 134 | # | ||||||
| 135 | #$self->agent->content =~ m! !sm |
||||||
| 136 | # or | ||||||
| 137 | #$self->agent->content =~ m! !sm |
||||||
| 138 | # or $self->maintenance; | ||||||
| 139 | }; | ||||||
| 140 | |||||||
| 141 | sub error_message { | ||||||
| 142 | 0 | 0 | 0 | 0 | my ($self) = @_; | ||
| 143 | #return unless $self->agent; | ||||||
| 144 | #die "No error condition detected in:\n" . $self->agent->content | ||||||
| 145 | # unless $self->error_page; | ||||||
| 146 | #if( | ||||||
| 147 | #$self->agent->content =~ m! \s*\s*(.*?)\s*\s* !sm |
||||||
| 148 | # or | ||||||
| 149 | #$self->agent->content =~ m! \s*(.*?)\s* !sm |
||||||
| 150 | # ) { return $1 } | ||||||
| 151 | # #or croak "No error message found in:\n" . $self->agent->content; | ||||||
| 152 | 0 | 0 | return '' | ||||
| 153 | }; | ||||||
| 154 | |||||||
| 155 | sub maintenance { | ||||||
| 156 | 1 | 1 | 1 | 19 | my ($self) = @_; | ||
| 157 | #return unless $self->agent; | ||||||
| 158 | ##$self->error_page and | ||||||
| 159 | #$self->agent->content =~ m!Sehr geehrter Online-Banking\s+Nutzer,\s+wegen einer hohen Auslastung kommt es derzeit im Online-Banking zu\s*längeren Wartezeiten.!sm | ||||||
| 160 | #or $self->agent->content =~ m! Wartung\b! | ||||||
| 161 | #or $self->agent->content =~ m! \s*\s*Diese Funktion steht auf Grund einer technischen St.*?rung derzeit leider nicht zur Verf.*?gung.*?\s* !sm # Testumgebung... |
||||||
| 162 | () | ||||||
| 163 | 1 | 3 | }; | ||||
| 164 | |||||||
| 165 | sub access_denied { | ||||||
| 166 | 3 | 3 | 0 | 1424 | my ($self) = @_; | ||
| 167 | 3 | 19 | $self->api->ua->status == 401 | ||||
| 168 | #if ($self->error_page) { | ||||||
| 169 | # my $message = $self->error_message; | ||||||
| 170 | # | ||||||
| 171 | # return ( | ||||||
| 172 | # $message =~ m!^Die Kontonummer ist nicht für das Internet Online-Banking freigeschaltet. Bitte verwenden Sie zur Freischaltung den Link "Online-Banking freischalten"\. \s*$!sm |
||||||
| 173 | # or $message =~ m!^Sie haben zu viele Zeichen in das Feld eingegeben. \s*$!sm |
||||||
| 174 | # or $message =~ m!^Die eingegebene Postbank Girokontonummer ist zu lang. Bitte überprüfen Sie Ihre Eingabe.$!sm | ||||||
| 175 | # or $message =~ m!^Die Anmeldung ist fehlgeschlagen. Bitte vergewissern Sie sich der Richtigkeit Ihrer Eingaben und f.*?hren Sie den Anmeldevorgang erneut durch.\s*$!sm | ||||||
| 176 | # ) | ||||||
| 177 | #} else { | ||||||
| 178 | # return; | ||||||
| 179 | #}; | ||||||
| 180 | }; | ||||||
| 181 | |||||||
| 182 | sub session_timed_out { | ||||||
| 183 | 0 | 0 | 1 | 0 | my ($self) = @_; | ||
| 184 | #$self->agent->content =~ /Die Sitzungsdaten sind ungültig, bitte führen Sie einen erneuten Login durch.\s+\(27000\)/; | ||||||
| 185 | () | ||||||
| 186 | 0 | 0 | }; | ||||
| 187 | |||||||
| 188 | sub select_function { | ||||||
| 189 | 0 | 0 | 1 | 0 | my ($self,$function) = @_; | ||
| 190 | 0 | 0 | 0 | if (! $self->session) { | |||
| 191 | 0 | 0 | $self->new_session; | ||||
| 192 | }; | ||||||
| 193 | croak "Unknown account function '$function'" | ||||||
| 194 | 0 | 0 | 0 | unless exists $functions{$function}; | |||
| 195 | 0 | 0 | my $method = $functions{ $function }; | ||||
| 196 | |||||||
| 197 | 0 | 0 | my $res = $self->session->navigate($method); | ||||
| 198 | 0 | 0 | $res | ||||
| 199 | }; | ||||||
| 200 | |||||||
| 201 | sub close_session { | ||||||
| 202 | 2 | 2 | 1 | 2538 | my ($self) = @_; | ||
| 203 | 2 | 24 | $self->session(undef); | ||||
| 204 | 2 | 155 | $self->api(undef); | ||||
| 205 | 2 | 1690 | 1 | ||||
| 206 | }; | ||||||
| 207 | |||||||
| 208 | sub finanzstatus { | ||||||
| 209 | 3 | 3 | 0 | 9 | my( $self ) = @_; | ||
| 210 | 3 | 100 | 20 | $self->new_session unless $self->session; | |||
| 211 | 3 | 28 | my $finanzstatus = $self->session->navigate( | ||||
| 212 | class => 'Finance::Bank::Postbank_de::APIv1::Finanzstatus', | ||||||
| 213 | path => ['banking_v1' => 'financialstatus'] | ||||||
| 214 | ); | ||||||
| 215 | } | ||||||
| 216 | |||||||
| 217 | sub _build_account_numbers { | ||||||
| 218 | 2 | 2 | 8 | my ($self,%args) = @_; | |||
| 219 | |||||||
| 220 | 2 | 8 | my $finanzstatus = $self->finanzstatus; | ||||
| 221 | 2 | 103 | (my $bp) = $finanzstatus->get_businesspartners; # always take the first... | ||||
| 222 | 2 | 151 | my %numbers; | ||||
| 223 | # this currently includes the credit card numbers ... | ||||||
| 224 | 2 | 17 | for my $acc ( $bp->get_accounts() ) { | ||||
| 225 | 14 | 100 | 100 | 90 | $numbers{ $acc->iban } = $acc if (!$acc->is_depot and !$acc->is_mortgage); | ||
| 226 | }; | ||||||
| 227 | |||||||
| 228 | 2 | 122 | return $self->_account_numbers( \%numbers ); | ||||
| 229 | } | ||||||
| 230 | |||||||
| 231 | sub account_numbers { | ||||||
| 232 | 1 | 1 | 1 | 7 | my ($self,%args) = @_; | ||
| 233 | |||||||
| 234 | 1 | 33 | 10 | my $n = $self->_account_numbers || $self->_build_account_numbers; | |||
| 235 | |||||||
| 236 | 1 | 3 | sort keys %{ $n }; | ||||
| 1 | 12 | ||||||
| 237 | }; | ||||||
| 238 | |||||||
| 239 | sub get_account_statement { | ||||||
| 240 | 11 | 11 | 1 | 1003455 | my ($self,%args) = @_; | ||
| 241 | |||||||
| 242 | #my $past_days = $args{past_days} || $self->{past_days}; | ||||||
| 243 | #if($past_days) { | ||||||
| 244 | # my ($day, $month, $year) = split/\./, $agent->current_form->value('umsatzanzeigeGiro:salesForm:umsatzFilterOptionenAufklappbarSuchfeldPanel:accordion:vonBisDatum:datumForm:bisGruppe:bisDatum'); | ||||||
| 245 | # my $end_epoch = timegm(0, 0, 0, $day, $month-1, $year); | ||||||
| 246 | # my $from_date = strftime '%d.%m.%Y', localtime($end_epoch-($past_days-1)*60*60*24); | ||||||
| 247 | # $agent->current_form->value('umsatzanzeigeGiro:salesForm:umsatzFilterOptionenAufklappbarSuchfeldPanel:accordion:vonBisDatum:datumForm:vonGruppe:vonDatum' => $from_date); | ||||||
| 248 | #}; | ||||||
| 249 | |||||||
| 250 | 11 | 66 | 177 | my $accounts = $self->_account_numbers || $self->_build_account_numbers; | |||
| 251 | |||||||
| 252 | 11 | 100 | 104 | if( ! $args{ account_number }) { | |||
| 253 | # Hopefully we only got one account (?!) | ||||||
| 254 | 1 | 5 | ($args{ account_number }) = keys %$accounts; | ||||
| 255 | }; | ||||||
| 256 | |||||||
| 257 | 11 | 61 | my $account = $accounts->{ $args{ account_number }}; | ||||
| 258 | |||||||
| 259 | #if (exists $args{account_number}) { | ||||||
| 260 | # $self->log("Getting account statement for $args{account_number}"); | ||||||
| 261 | # # Load the account numbers if not already loaded | ||||||
| 262 | # $self->account_numbers; | ||||||
| 263 | # if(! exists $self->{account_numbers}->{$args{account_number}}) { | ||||||
| 264 | # croak "Unknown account number '$args{account_number}'"; | ||||||
| 265 | # }; | ||||||
| 266 | # my $index = $self->{account_numbers}->{$args{account_number}}; | ||||||
| 267 | # $agent->current_form->param( 'selectForm:kontoauswahl' => $index ); | ||||||
| 268 | #} else { | ||||||
| 269 | # my @accounts = $agent->current_form->value('selectForm:kontoauswahl'); | ||||||
| 270 | # $self->log("Getting account statement via default (@accounts)"); | ||||||
| 271 | #}; | ||||||
| 272 | 11 | 108 | my $content = $account->transactions_csv(); | ||||
| 273 | 11 | 100 | 1819 | if( $args{ file }) { | |||
| 274 | open my $fh, '>', $args{ file } | ||||||
| 275 | 1 | 50 | 131 | or croak "Couldn't create '$args{ file }': $!"; | |||
| 276 | 1 | 20 | binmode $fh, ':encoding(UTF-8)'; | ||||
| 277 | 1 | 238 | print $fh $content; | ||||
| 278 | }; | ||||||
| 279 | #if ($agent->status == 200) { | ||||||
| 280 | 11 | 43 | my $result = $content; | ||||
| 281 | # Result is in UTF-8 | ||||||
| 282 | 11 | 151 | return Finance::Bank::Postbank_de::Account->parse_statement(content => $result); | ||||
| 283 | }; | ||||||
| 284 | |||||||
| 285 | sub unread_messages { | ||||||
| 286 | 1 | 1 | 1 | 12 | my( $self )= @_; | ||
| 287 | 1 | 5 | $self->finanzstatus->available_messages | ||||
| 288 | } | ||||||
| 289 | |||||||
| 290 | 1; | ||||||
| 291 | __END__ |