The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Business::CPI::Gateway::Base;
# ABSTRACT: Father of all gateways
use Moo;
use Locale::Currency ();
use Data::Dumper;
use Carp qw/croak/;

with 'Business::CPI::Role::Gateway::Base';

our $VERSION = '0.918'; # VERSION

has receiver_id => (
    is => 'ro',
);

has checkout_url => (
    is => 'rw',
);

has checkout_with_token => (
    is => 'ro',
    default => sub { 0 },
);

has currency => (
    isa => sub {
        my $curr = uc($_[0]);

        for (Locale::Currency::all_currency_codes()) {
            return 1 if $curr eq uc($_);
        }

        die "Must be a valid currency code";
    },
    coerce => sub { uc $_[0] },
    is => 'ro',
);

sub new_account {
    my ($self, $account) = @_;

    return $self->account_class->new(
        _gateway => $self,
        %$account
    );
}

sub new_cart {
    my ( $self, $info ) = @_;

    if ($self->log->is_debug) {
        $self->log->debug("Building a cart with: " . Dumper($info));
    }

    my @items     = @{ delete $info->{items}     || [] };
    my @receivers = @{ delete $info->{receivers} || [] };

    my $buyer_class = $self->buyer_class;
    my $cart_class  = $self->cart_class;

    # We might be using a more generic Account class
    if ($buyer_class->does('Business::CPI::Role::Account')) {
        $info->{buyer}{_gateway} = $self;
    }

    $self->log->debug(
        "Loaded buyer class $buyer_class and cart class $cart_class."
    );

    my $buyer = $buyer_class->new( delete $info->{buyer} );

    $self->log->info("Built cart for buyer " . $buyer->email);

    my $cart = $cart_class->new(
        _gateway => $self,
        buyer    => $buyer,
        %$info,
    );

    for (@items) {
        $cart->add_item($_);
    }

    for (@receivers) {
        $cart->add_receiver($_);
    }

    return $cart;
}

sub map_object {
    my ($self, $map, $obj) = @_;

    my @result;

    while (my ($bcpi_key, $gtw_key) = each %$map) {
        my $value = $obj->$bcpi_key;
        next unless $value;

        my $name = $gtw_key;

        if (ref $gtw_key) {
            $name  = $gtw_key->{name};
            $value = $gtw_key->{coerce}->($name);
        }

        push @result, ( $name, $value );
    }

    return @result;
}

sub get_notification_details { shift->_unimplemented }

sub query_transactions { shift->_unimplemented }

sub get_transaction_details { shift->_unimplemented }

sub notify { shift->_unimplemented }

sub get_checkout_code { shift->_unimplemented }

sub _unimplemented {
    my $self = shift;
    die "Not implemented.";
}

around BUILDARGS => sub {
    my $orig = shift;
    my $self = shift;
    my $args = $self->$orig(@_);

    if ($args->{receiver_email}) {
        croak 'receiver_email attribute has been removed - use receiver_id instead';
    }

    return $args;
};

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

Business::CPI::Gateway::Base - Father of all gateways

=head1 VERSION

version 0.918

=head1 ATTRIBUTES

=head2 driver_name

The name of the driver for this gateway. This is built automatically, but can
be customized.

Example: for C<Business::CPI::Gateway::TestGateway>, the driver name will be
C<TestGateway>.

=head2 log

Provide a logger to the gateway. It's the user's responsibility to configure
the logger. By default, nothing is logged. You could set this to a
L<Log::Log4perl> object, for instance, to get full logging.

=head2 item_class

The class for the items (products) being purchased. Defaults to
Business::CPI::${driver_name}::Item if it exists, or
L<Business::CPI::Base::Item> otherwise.

=head2 cart_class

The class for the shopping cart (the complete order). Defaults to
Business::CPI::${driver_name}::Cart if it exists, or
L<Business::CPI::Base::Cart> otherwise.

=head2 buyer_class

The class for the buyer (the sender). Defaults to
Business::CPI::${driver_name}::Buyer if it exists, or
L<Business::CPI::Base::Buyer> otherwise.

=head2 account_class

The class for the accounts. Defaults to Business::CPI::${driver_name}::Account
if it exists, or L<Business::CPI::Base::Account> otherwise.

=head2 account_address_class

The class for the addresses for the accounts. Defaults to
Business::CPI::${driver_name}::Account::Address if it exists, or
L<Business::CPI::Base::Account::Address> otherwise.

=head2 account_business_class

The class for the business information of accounts. Defaults to
Business::CPI::${driver_name}::Account::Business if it exists, or
L<Business::CPI::Base::Account::Business> otherwise.

=head2 receiver_id

ID, login or e-mail of the business owner. The way the gateway uniquely
identifies the account owner.

=head2 currency

Currency code, such as BRL, EUR, USD, etc.

=head2 notification_url

The url for the gateway to postback, notifying payment changes.

=head2 return_url

The url for the customer to return to, after they finished the payment.

=head2 checkout_with_token

Boolean attribute to determine whether the form will hold the entire cart, or
it will use the payment token generated for it. Defaults to false.

=head2 checkout_url

The url the application will post the form to. Defined by the gateway.

=head1 METHODS

=head2 new_cart

Creates a new L<Business::CPI::Role::Cart> connected to this gateway.

=head2 new_account

Creates a new instance of an account. In general, you shouldn't need to use
this, except for testing. Use C<create_account>, instead, if your driver
provides it.

=head2 get_checkout_code

Generates a payment token for a given cart. Do not call this method directly.
Instead, see L<Business::CPI::Role::Cart/get_checkout_code>.

=head2 get_notification_details

Get the payment notification (such as PayPal's IPN), and return a hashref with
the details.

=head2 query_transactions

Search past transactions.

=head2 get_transaction_details

Get more details about a given transaction.

=head2 notify

This is supposed to be called when the gateway sends a notification about a
payment status change to the application. Receives the request as a parameter
(in a CGI-compatible format), and returns data about the payment. The format is
still under discussion, and is soon to be documented.

=head2 map_object

Helper method for get_hidden_inputs to translate between Business::CPI and the
gateway, using methods like checkout_form_items_map, checkout_form_buyer_map,
etc.

=for Pod::Coverage BUILDARGS

=head1 AUTHOR

André Walker <andre@andrewalker.net>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2014 by André Walker.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut