Finance-Bank-Natwest

 view release on metacpan or  search on metacpan

README  view on Meta::CPAN

NAME
    Finance::Bank::Natwest - Check your Natwest bank accounts from Perl

DESCRIPTION
    This module provides a rudimentary interface to the Natwest online
    banking system at C<https://www.nwolb.com/>. You will need
    either C<Crypt::SSLeay> or C<IO::Socket::SSL> installed for HTTPS
    support to work with LWP.

SYNOPSIS
      my $nw = Finance::Bank::Natwest->new( credentials => 'Constant',
                                            credentials_options => {
					       dob => '010179',
					       uid => '0001',
					       password => 'Password',
					       pin => '4321' } );

      my @accounts = $nw->accounts;

      foreach (@accounts) {
            printf "%25s : %6s / %8s : GBP %8.2f\n",
              $_->{name}, $_->{sortcode}, $_->{account}, $_->{available};

lib/Finance/Bank/Natwest.pm  view on Meta::CPAN


=head1 DESCRIPTION

This module provides a rudimentary interface to the Natwest online
banking system at C<https://www.nwolb.com/>. You will need
either C<Crypt::SSLeay> or C<IO::Socket::SSL> installed for HTTPS
support to work with LWP.

=head1 SYNOPSIS

  my $nw = Finance::Bank::Natwest->new( credentials => 'Constant',
                                        credentials_options => {
					   dob => '010179',
					   uid => '0001',
					   password => 'Password',
					   pin => '4321' } );

  my @accounts = $nw->accounts;

  foreach (@accounts) {
        printf "%25s : %6s / %8s : GBP %8.2f\n",
          $_->{name}, $_->{sortcode}, $_->{account}, $_->{available};
  }

=head1 METHODS

=over 4 

=item B<new>

  my $nw = Finance::Bank::Natwest->new( credentials => 'Constant',
                                        credentials_options => {
					   dob => '010179',
					   uid => '0001',
					   password => 'Password',
					   pin => '4321' }
  );

  # Or create the credentials object ourselves
  my $credentials = Finance::Bank::Natwest::CredentialsProvider::Constant->new(
     dob => '010179', uid => '0001', password => 'Password', pin => '4321' );

  my $nw = Finance::Bank::Natwest->new( credentials => $credentials );


C<new> can be called in two different ways. It can take a single parameter,
C<credentials>, which will accept an already created credentials object, of type 
C<Finance::Bank::Natwest::CredentialsProvider::*>. Alternatively, it can take two
parameters, C<credentials> and C<credentials_options>. In this case 
C<credentials> is the name of a credentials class to create an instance of, and
C<credentials_options> is a hash of the options to pass-through to the
constructor of the chosen class.

If the second form of C<new> is being used, and the chosen class is I<not> one
of the ones supplied as standard then it will need to be C<required> first.

If any errors occur then C<new> will C<croak>.

=cut

use constant URL_ROOT => 'https://www.nwolb.com';

lib/Finance/Bank/Natwest.pm  view on Meta::CPAN

};

=item B<accounts>

  my @accounts = $nw->accounts;

  # Or get a list ref instead
  my $accounts = $nw->accounts;

Returns a list containing a summary of any accounts available from the
supplied credentials. Each item in the list is a hash reference that holds
summary information for a single account, and contains this data:

=over 4

=item B<name> - the name of the account

=item B<account> - the account number

=item B<sortcode>

lib/Finance/Bank/Natwest/Connection.pm  view on Meta::CPAN

    };


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

    my $self = bless {}, $class;

    $self->{url_base} = $opts{url_base} || Finance::Bank::Natwest->url_base;

    $self->_set_credentials( %opts );
    $self->_new_ua( %opts );
    
    return $self;
}

sub _new_ua{
    my ($self, %opts) = @_;

    my %proxy;

lib/Finance/Bank/Natwest/Connection.pm  view on Meta::CPAN

        keep_alive => 1,
        timeout => 30,
        cookie_jar => {},
        requests_redirectable => [ 'GET', 'HEAD', 'POST' ],
        agent => "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"
    );

    $self->{ua}->proxy('https', $proxy{proxy}) if exists $proxy{proxy};
}

sub _set_credentials{
    my ($self, %opts) = @_;

    croak "Must provide either a premade credentials object or ".
          "a class name together with options, stopped" if
        !exists $opts{credentials};

    if (ref($opts{credentials})) {
        croak "Can't accept credential options if supplying a premade ".
              "credentials object, stopped" if
            exists $opts{credentials_options};

        croak "Not a valid credentials object, stopped" unless
            $self->_isa_credentials($opts{credentials});

        $self->{credentials} = $opts{credentials};
    } else {
        croak "Must provide credential options unless suppying a premade ".
              "credentials object, stopped" if
            !exists $opts{credentials_options};

        $self->{credentials} =
            $self->_new_credentials(
                $opts{credentials}, $opts{credentials_options}
            );
    };
}

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

    croak "Invalid class name, stopped" if
        $class !~ /^(?:\w|::)+$/;
    
    my $full_class = "Finance::Bank::Natwest::CredentialsProvider::$class";
    
    eval "local \$SIG{'__DIE__'}; 
          local \$SIG{'__WARN__'}; 
          require $full_class;
         ";
    croak "Not a valid credentials class, stopped"
        if $@;

    croak "Not a valid credentials class, stopped"
        unless $self->_isa_credentials($full_class);

    {
        local $Carp::CarpLevel = $Carp::CarpLevel + 1;
        return $full_class->new(%{$options});
    }
}

sub _isa_credentials{
    my ($self, $credentials) = @_;

    my @required_subs = qw( new get_start get_stop get_identity get_pinpass );
  
    foreach my $sub (@required_subs) {
        return unless defined eval {
            local $SIG{'__DIE__'};
            local $SIG{'__WARN__'};
            $credentials->can($sub);
        };
    }

    return 1;
}

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

    my $page;

    $self->{login_ok} = 0;
    $self->{in_login} = 1;
    delete $self->{rb_id};

    $self->{credentials}->get_start();

    my $identity = $self->{credentials}->get_identity();

    ($self->{rb_id}, $page) = $self->post( 'logon.asp',
        {   DBIDa => $identity->{dob}, DBIDb => $identity->{uid},
            radType => '', scriptingon => 'yup' } );

    croak "Error during login process. " . 
          "The website is temporarily unavailable, stopped" if
        $page =~ m|Service Temporarily Unvailable|i;

    croak "Error during login process, stopped" if

lib/Finance/Bank/Natwest/Connection.pm  view on Meta::CPAN

                  |ix;
    
    croak "Error during login process. " .
          "Unrecognised password request ($1, $2, $3), stopped" unless
        exists POSS_PASS->{$1} && 
        exists POSS_PASS->{$2} && 
        exists POSS_PASS->{$3};
    
    my $pass_chars = [ POSS_PASS->{$1}, POSS_PASS->{$2}, POSS_PASS->{$3} ];

    my $pinpass = $self->{credentials}->get_pinpass( $pin_digits, $pass_chars );
    $self->{credentials}->get_stop();

    $page = $self->post('Logon-PinPass.asp', 
        {   pin1 => $pinpass->{pin}[0], 
            pin2 => $pinpass->{pin}[1],
            pin3 => $pinpass->{pin}[2],
            pass1 => $pinpass->{password}[0],
            pass2 => $pinpass->{password}[1],
            pass3 => $pinpass->{password}[2],
            buttonComplete => 'Submitted',
            buttonFinish => 'Finish' } );

lib/Finance/Bank/Natwest/CredentialsProvider/Callback.pm  view on Meta::CPAN


use vars qw( $VERSION );
$VERSION = '0.03';

=head1 NAME

Finance::Bank::Natwest::CredentialsProvider::Callback - Credentials provider that uses a callback to gather the required information

=head1 DESCRIPTION

CredentialsProvider module that uses a callback to retrieve the credentials.

=head1 SYNOPSIS

  my $credentials = Finance::Bank::Natwest::CredentialsProvider::Callback->new(
     callback => \&credentials_callback
  );

=head1 METHODS

=over 4

=item B<new>

  my $credentials = Finance::Bank::Natwest::CredentialsProvider::Callback->new(
     callback => \&credentials_callback
  );

  # Or we can also provide an ID to pass into the callback routine
  my $credentials = Finance::Bank::Natwest::CredentialsProvider::Callback->new(
     callback => \&credentials_callback, id => 1
  );

If C<id> is provided then it must be a simple scalar, and not a reference.

=cut

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

    my $self = bless {}, $class;

lib/Finance/Bank/Natwest/CredentialsProvider/Constant.pm  view on Meta::CPAN

package Finance::Bank::Natwest::CredentialsProvider::Constant;

use Carp;

use vars qw( $VERSION );
$VERSION = '0.03';

=head1 NAME

Finance::Bank::Natwest::CredentialsProvider::Constant - Static credentials provider

=head1 DESCRIPTION

CredentialsProvider module for static credentials.

=head1 SYNOPSIS

  my $credentials = Finance::Bank::Natwest::CredentialsProvider::Constant->new(
     dob => '010179', uid => '0001', password => 'Password', pin => '4321'
  );

=head1 METHODS

=over 4

=item B<new>

  my $credentials = Finance::Bank::Natwest::CredentialsProvider::Constant->new(
     dob => '010179', uid => '0001', password => 'Password', pin => '4321'
  );

  # Or we can combine the dob and uid together
  my $credentials = Finance::Bank::Natwest::CredentialsProvider::Constant->new(
     customer_no => '0101790001', password => 'Password', pin => '4321'
  );


All the parameters are mandatory in both forms of the constructor.

=cut

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

t/callback.t  view on Meta::CPAN

        callback => $callback_info->[1] );

    for my $callback (@{$callback_info->[0]}) {
        my $callback_id = $callback->[0];
        my $callback_identity = $callback->[1];
        my $callback_pinpass = $callback->[2];

        if (!defined $callback_identity) {
            dies_ok {
                $provider->get_start( id => $callback_id );
            } 'Invalid id or invalid credentials: expected to fail';
        } else {
            $provider->get_start( id => $callback_id );

            is_deeply( $provider->get_identity(), $callback_identity,
                'Got expected identity' );

            is_deeply( $provider->get_pinpass( 
                [0..@{$callback_pinpass->{pin}}-1],
                [0..@{$callback_pinpass->{password}}-1] ),
                $callback_pinpass, 

t/connection.t  view on Meta::CPAN

use Carp;
use Test::More tests => 15;
use Test::Exception;

use Mock::NatwestWebServer;
my $nws = Mock::NatwestWebServer->new();

use_ok( 'Finance::Bank::Natwest::Connection' );

for (   {},
        { credentials => 'Constant' },
        { credentials => 'UnknownCP' },
        { credentials => 'UnknownCP', credentials_options => {} }, 
        { credentials => bless {}, 'YetAnotherUnknownCP' },
        { credentials => {} },
        { credentials_options => {} } ) {
    dies_ok {
        my $nwb = Finance::Bank::Natwest::Connection->new(%{$_});
    } 'invalid credential parameters: expected to fail';
}
    
{
    my $nwb;

    ok(
        $nwb = Finance::Bank::Natwest::Connection->new(
            credentials => 'Constant', 
	    credentials_options => { customer_no => '0101790001',
	                             password => 'Password',
				     pin => '1234' }
	),
        'valid credentials - getting ::Connection to create credentials object'
    );

    isa_ok( $nwb, 'Finance::Bank::Natwest::Connection' );

    foreach my $method (qw( login post )) {
        can_ok( $nwb, $method );
    }

    is( $nws->next_call(), undef, 'nothing but new() called yet' );
    $nws->clear();
}

{
    my $creds = Finance::Bank::Natwest::CredentialsProvider::Constant->new(
        customer_no => '0101790001', password => 'Password', pin => '1234'
    );

    ok(
        my $nwb = Finance::Bank::Natwest::Connection->new(
                     credentials => $creds ), 
        'valid credentials - providing premade credentials object' 
    );

    $nws->add_account( dob => '010179', uid => '0001',
                       pin => '1234', pass => 'Password' );
    $nwb->login();
    ok( $nwb->{login_ok}, 'Logged in successfully' );
}

t/constant.t  view on Meta::CPAN

        %{$valid_details[0]}
    );

    isa_ok( $provider, 'Finance::Bank::Natwest::CredentialsProvider::Constant' );

    foreach my $method (qw( get_start get_stop get_identity get_pinpass )) {
        can_ok( $provider, $method );
    }
}

foreach my $credentials (@invalid_details) {
    dies_ok { 
        my $provider = 
            Finance::Bank::Natwest::CredentialsProvider::Constant->new(
                %{$credentials} );
    } 'invalid credentials: expected to fail';
}

foreach my $credentials (@valid_details) {
    my $provider = Finance::Bank::Natwest::CredentialsProvider::Constant->new(
        %{$credentials}
    );

    $provider->get_start();

    is_deeply( $provider->get_identity(), 
        { uid => '0001', dob => '010179' },
        'Got expected identity' );

    is_deeply( $provider->get_pinpass( [0,1,2,3], [0,1,2,3,4,5,6,7] ),
        { pin => ['4','3','2','1'],

t/fbn_creation.t  view on Meta::CPAN

dies_ok {
   my $fbn = Finance::Bank::Natwest->new();
} 'invalid credential parameters: expected to fail';

my $cred_obj = Finance::Bank::Natwest::CredentialsProvider::Constant->new(
    customer_no => '0101790001', password => 'Password', pin => '4321'
);

dies_ok {
   my $fbn = Finance::Bank::Natwest->new( 
      credentials => $cred_obj,
      credentials_options => undef
   );
} 'invalid credential parameters: expected to fail';

dies_ok {
   my $fbn = Finance::Bank::Natwest->new( 
      credentials => $cred_obj,
      credentials_options => {}
   );
} 'invalid credential parameters: expected to fail';

dies_ok {
   my $fbn = Finance::Bank::Natwest->new( 
      credentials => $cred_obj,
      credentials_options => { customer_no => '0101790001',
                               password => 'Password',
			       pin => '4321'} 
   );
} 'invalid credential parameters: expected to fail';

dies_ok {
   my $fbn = Finance::Bank::Natwest->new( credentials => 'Constant' );
} 'invalid credential parameters: expected to fail';

dies_ok {
   my $fbn = Finance::Bank::Natwest->new( credentials => 'Callback' );
} 'invalid credential parameters: expected to fail';

dies_ok {
   my $fbn = Finance::Bank::Natwest->new( credentials => 'GPG' );
} 'invalid credential parameters: expected to fail';

dies_ok {
   my $fbn = Finance::Bank::Natwest->new(
      credentials => 'Constant',
      credentials_options => {}
   );
} 'invalid credential parameters: expected to fail';

dies_ok {
   my $fbn = Finance::Bank::Natwest->new(
      credentials => 'Callback',
      credentials_options => {}
   );
} 'invalid credential parameters: expected to fail';

dies_ok {
   my $fbn = Finance::Bank::Natwest->new( credentials_options => {} );
} 'invalid credentials parameters: expected to fail';


{
   my $fbn = Finance::Bank::Natwest->new( credentials => 'Constant',
                                          credentials_options => { 
                                             customer_no => '0101790001',
                                             password => 'Password',
				             pin => '4321'
				       } );

   isa_ok( $fbn, 'Finance::Bank::Natwest' );

   foreach my $method (qw( accounts )) {
      can_ok( $fbn, $method );
   }

t/fbr_creation.t  view on Meta::CPAN

dies_ok {
   my $fbn = Finance::Bank::RBoS->new();
} 'invalid credential parameters: expected to fail';

my $cred_obj = Finance::Bank::Natwest::CredentialsProvider::Constant->new(
    customer_no => '0101790001', password => 'Password', pin => '4321'
);

dies_ok {
   my $fbn = Finance::Bank::RBoS->new( 
      credentials => $cred_obj,
      credentials_options => undef
   );
} 'invalid credential parameters: expected to fail';

dies_ok {
   my $fbn = Finance::Bank::RBoS->new( 
      credentials => $cred_obj,
      credentials_options => {}
   );
} 'invalid credential parameters: expected to fail';

dies_ok {
   my $fbn = Finance::Bank::RBoS->new( 
      credentials => $cred_obj,
      credentials_options => { customer_no => '0101790001',
                               password => 'Password',
			       pin => '4321'} 
   );
} 'invalid credential parameters: expected to fail';

dies_ok {
   my $fbn = Finance::Bank::RBoS->new( credentials => 'Constant' );
} 'invalid credential parameters: expected to fail';

dies_ok {
   my $fbn = Finance::Bank::RBoS->new( credentials => 'Callback' );
} 'invalid credential parameters: expected to fail';

dies_ok {
   my $fbn = Finance::Bank::RBoS->new( credentials => 'GPG' );
} 'invalid credential parameters: expected to fail';

dies_ok {
   my $fbn = Finance::Bank::RBoS->new(
      credentials => 'Constant',
      credentials_options => {}
   );
} 'invalid credential parameters: expected to fail';

dies_ok {
   my $fbn = Finance::Bank::RBoS->new(
      credentials => 'Callback',
      credentials_options => {}
   );
} 'invalid credential parameters: expected to fail';

dies_ok {
   my $fbn = Finance::Bank::RBoS->new( credentials_options => {} );
} 'invalid credentials parameters: expected to fail';


{
   my $fbn = Finance::Bank::RBoS->new( credentials => 'Constant',
                                          credentials_options => { 
                                             customer_no => '0101790001',
                                             password => 'Password',
				             pin => '4321'
				       } );

   isa_ok( $fbn, 'Finance::Bank::RBoS' );

   foreach my $method (qw( accounts )) {
      can_ok( $fbn, $method );
   }



( run in 0.862 second using v1.01-cache-2.11-cpan-4d50c553e7e )