Akado-Account

 view release on metacpan or  search on metacpan

lib/Akado/Account.pm  view on Meta::CPAN

package Akado::Account;
{
  $Akado::Account::VERSION = '1.2.0';
}

# ABSTRACT: get internet provider Akado account info

use strict;
use warnings FATAL => 'all';
use utf8;

use Carp;
use Digest::MD5 qw(md5_hex);
use HTTP::Request::Common;
use LWP;
use XML::XPath;


sub new {
    my ($class, $self) = @_;

    croak 'No login specified, stopped' unless $self->{login};
    croak 'No password specified, stopped' unless $self->{password};

    $self->{site} ||= 'https://office.akado.ru/';

    bless($self, $class);
    return $self;
}


sub get_balance {
    my ($self) = @_;

    my $data = $self->_get_cached_parsed_data();
    return $data->{balance};
}


sub get_next_month_payment {
    my ($self) = @_;

    my $data = $self->_get_cached_parsed_data();
    return $data->{next_month_payment};
}


sub _get_cached_parsed_data {
    my ($self) = @_;

    if (not defined $self->{_parsed_data}) {
        my $xml = $self->_get_full_account_info_xml();
        $self->{_parsed_data} = $self->_parse_xml($xml);
    }

    return $self->{_parsed_data};
}


sub _get_full_account_info_xml {
    my ($self) = @_;

    my $browser = LWP::UserAgent->new;
    $browser->agent("Akado::Account/$Akado::Account::VERSION");
    $browser->cookie_jar( {} );

    # At first we need to login to the site.
    # Here we POST login/password and recieve session cookies that are stored
    # in the UserAgent cookie_jar.
    my $auth_response = $self->_get_auth_response($browser);

    # Here we get account data using session cookies that we got at the
    # previous step
    my $data_response = $self->_get_data_response($browser);

    my $xml = $data_response->decoded_content;

    return $xml;
}


sub _parse_xml {
    my ($self, $xml) = @_;

    my $xp = XML::XPath->new( xml => $xml );

    my $balance = $xp->findnodes('//bill[contains(@description, "Остаток на счете на")]/@amount')->[0]->getNodeValue();
    my $next_month_payment = $xp->findnodes('//bill[@description="Стоимость услуг в следующем календарном месяце"]/@amount')->[0]->getNodeValue();

    my $parsed_account_info = {
        balance => $balance,
        next_month_payment => $next_month_payment,
    };

    return $parsed_account_info;
}


sub _get_auth_response {
    my ($self, $browser) = @_;

    my $url = $self->{site} . "/user/login.xml";

    my $request = POST($url,
        Content => [
            login    => $self->{login},
            password => $self->{password},
        ]
    );

    my $response = $browser->request($request);
    $self->_check_response($response);

    return $response;
}


sub _get_data_response {
    my ($self, $browser) = @_;

    # To get from Akado site data in xml format we need to add cookie render
    # with the value 'xml'
    $browser->{cookie_jar}->set_cookie(
        0,        # version
        'render', # key
        'xml',    # value
        '/',      # $path
        $self->_get_domain_from_cookies($browser->{cookie_jar}), # domain
    );

    my $url = $self->{site} . "/finance/display.xml";

    my $request = HTTP::Request->new(
        'GET',
        $url,
    );

    my $response = $browser->request($request);
    $self->_check_response($response);

    return $response;
}


sub _get_domain_from_cookies {
    my ($self, $cookies) = @_;

    my $domain;

    $cookies->scan(
        sub {
            $domain = $_[4];
        }
    );

    return $domain
}


sub _check_response {
    my ($self, $response) = @_;

    my $url = scalar $response->request->uri->canonical;
    if ($response->is_error) {
        croak "Can't get url '$url'. Got error "
            . $response->status_line;
    }

    return '';
}


1;

__END__

=pod

=encoding UTF-8

=head1 NAME

Akado::Account - get internet provider Akado account info

=head1 VERSION

version 1.2.0

=head1 SYNOPSIS

Akado is the internet provider that works in Moscow, Russia.
L<http://www.akado.ru/>

Every Akado customer has access to the site L<https://office.akado.ru/> where
he can see his account info. This module creates Perl object that gets account
info from that site.

Unfortunately Akdado account site has no API, so this module acts as a browser
to get needed info.

Every module method dies in case of error.

=head1 DESCRIPTION

Akado::Account version numbers uses Semantic Versioning standart.
Please visit L<http://semver.org/> to find out all about this great thing.

=head1 METHODS

=head2 new

This a constuctor. It creates object. The constractor will not access the
account site. All network interaction is made in the methods that return data.

    my $aa = Akado::Account->new({
        login => $login,
        password => $password,
    });

=head2 get_balance

It will return number. The number is the sum of money that is left on the
user account. The currencty is RUB (Russian rouble).

    say $aa->get_balance();     # will print '749.82', or something like this

If the object hasn't accessed the Akado account site
L<https://office.akado.ru/> since the object was created, the method will
access site, get data from it and store it in the object. The object will
access Akado site only once, after saving data in the object all methods use
that cached data.

=head2 get_next_month_payment

It will return number. The number is the sum of money that the user will have
to pay for the next month. The currencty is RUB (Russian rouble).

    say $aa->get_next_month_payment();

If the object hasn't accessed the Akado account site
L<https://office.akado.ru/> since the object was created, the method will
access site, get data from it and store it in the object. The object will
access Akado site only once, after saving data in the object all methods use
that cached data.

=begin comment _get_cached_parsed_data

B<Get:> 1) $self

B<Return:> 1) $parsed_data

This is private method that should not be used outside the object.

The method checks if the object has already accessed Akado site, if it hasn't
the method gets the data from site, parses it, stores parsed data in the
object and returnes the stored data.

It the object already had accessed Akado site the method will just return
stored parsed data.

Sample of what it can return:

    {
        balance            => 749.82,
        date               => "2012-09-29",
        next_month_payment => 779,
    }

=end comment

=begin comment _get_full_account_info_xml

B<Get:> 1) $self

B<Return:> 1) $xml

Metod returns xml with user account info. Because the Akado site hasn't got



( run in 0.534 second using v1.01-cache-2.11-cpan-4ef0a570458 )