The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Business::OnlinePayment::Braintree;

use 5.006;
use strict;
use warnings;

use Business::OnlinePayment 3.01;
use Net::Braintree;

use base 'Business::OnlinePayment';

=head1 NAME

Business::OnlinePayment::Braintree - Online payment processing through Braintree

=head1 VERSION

Version 0.010

=cut

our $VERSION = '0.010';

=head1 SYNOPSIS

    use Business::OnlinePayment;

    $tx = new Business::OnlinePayment('Braintree',
                                      merchant_id => 'your merchant id',
                                      public_key => 'your public key',
                                      private_key => 'your private key',
                                     );

    $tx->test_transaction(1); # sandbox transaction for development and tests
  
    $tx->content(amount => 100,
                 card_number => '4111 1111 1111 1111',
                 expiration => '1212');

    $tx->submit();

    if ($tx->is_success) {
        print "Card processed successfully: " . $tx->authorization . "\n";
    } else {
        print "Card was rejected: " . $tx->error_message . "\n";
    }

=head1 DESCRIPTION

Online payment processing through Braintree based on L<Net::Braintree>.

The documentation for L<Net::Braintree> is located at
L<https://www.braintreepayments.com/docs/perl>.

=head1 NOTES

This is supposed to cover the complete Braintree Perl API finally.

=head1 METHODS

=head2 submit

Submits transaction to Braintree gateway.

=cut

sub submit {
    my $self = shift;
    my $config = Net::Braintree->configuration;
    my %content = $self->content;
    my ($action, $result, $transaction, $result_code);

    # sandbox vs production
    if ($self->test_transaction) {
	$config->environment('sandbox');
    }
    else {
	$config->environment('production');
    }

    # transaction
    $action = lc($content{action});

    if ($action eq 'normal authorization' ) {
        $result = $self->sale(1);
    }
    elsif ($action eq 'authorization only') {
        $result = $self->sale(0);
    }
    elsif ($action eq 'post authorization') {
        $result = Net::Braintree::Transaction->submit_for_settlement($content{order_number}, $content{amount});
    }
    elsif ($action eq 'credit' ) {
        $result = Net::Braintree::Transaction->refund($content{order_number}, $content{amount});
    }
    else {
        $self->error_message( "unsupported action for Braintree: $content{action}" );
        return 0;
    }

    my %result_codes = (
        2000 => 'declined',
        2001 => 'nsf',
        2002 => 'nsf',
        2003 => 'nsf',
        2010 => 'declined',
        2012 => 'declined',
        2013 => 'declined',
        2014 => 'declined',
        2022 => 'declined',
        2038 => 'declined',
        2041 => 'declined',
        2044 => 'declined',
        2046 => 'declined',
        2047 => 'pickup',
        2053 => 'stolen',
    );

    $transaction = $result->transaction;

    if ( $transaction ) {
        $result_code = $transaction->processor_response_code;

        $self->result_code($result_code);
        $self->order_number($transaction->id);
    }

    if ($result->is_success()) {
        $self->is_success(1);
        $self->authorization($transaction->id);
    }
    else {
        $self->is_success(0);
        $self->error_message($result->message);
        $self->failure_status($result_codes{$result_code})
          if ( $result_code && $result_codes{$result_code} );
    }
}

=head2 sale $submit

Performs sale transaction with Braintree. Used both
for settlement ($submit is a true value) and
authorization ($submit is a false value).

=cut

sub sale {
    my ($self, $submit) = @_;
    my %content = $self->content;

    # get rid of slash inside expiration value
    $content{expiration} =~ s%/%%;

    my %args = (
            amount => $content{amount},
            order_id => $content{invoice_number},
            credit_card => {
                number => $content{card_number},
                expiration_month => substr($content{expiration},0,2),
                expiration_year => substr($content{expiration},2,2),
                cvv => $content{cvv},
            },
            billing => {
                first_name => $content{first_name},
                last_name => $content{last_name},
                company => $content{company},
                street_address => $content{address},
                locality => $content{city},
                region => $content{state},
                postal_code => $content{zip},
                country_code_alpha2 => $content{country}
            },
            options => {
	            submit_for_settlement => $submit,
            }
        );

    if (exists $content{merchant_account_id}
        && $content{merchant_account_id}) {
        $args{merchant_account_id} = $content{merchant_account_id};
    }

    my $result = Net::Braintree::Transaction->sale(\%args);

    return $result;
}

=head2 set_defaults

Sets defaults for the Braintree merchant id, public and private key.

=cut
    
sub set_defaults {
    my ($self, %opts) = @_;
    my $config = Net::Braintree->configuration;

    $config->merchant_id($opts{merchant_id});
    $config->public_key($opts{public_key});
    $config->private_key($opts{private_key});

    return;
}

=head1 AUTHOR

Stefan Hornburg (Racke), C<< <racke at linuxia.de> >>

=head1 BUGS

Please report any bugs or feature requests to C<bug-business-onlinepayment-braintree at rt.cpan.org>, or through
the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Business-OnlinePayment-Braintree>.  I will be notified, and then you'll
automatically be notified of progress on your bug as I make changes.

You can find documentation for this module with the perldoc command.

    perldoc Business::OnlinePayment::Braintree


You can also look for information at:

=over 4

=item * Github issues (report bugs here)

L<https://github.com/interchange/Business-OnlinePayment-Braintree/issues>

=item * AnnoCPAN: Annotated CPAN documentation

L<http://annocpan.org/dist/Business-OnlinePayment-Braintree>

=item * CPAN Ratings

L<http://cpanratings.perl.org/d/Business-OnlinePayment-Braintree>

=item * Search CPAN

L<http://search.cpan.org/dist/Business-OnlinePayment-Braintree/>

=back


=head1 ACKNOWLEDGEMENTS

Grant for the following enhancements (RT #88525):

=over 4

=item billing address transmission

=item order number transmission

=item refund ability

=item added submit_for_settlement to complete the "sale" action

=back

Peter Mottram for the following enhancements (GH #5):

=over 4

=item Failure status

Set failure status from error codes provided by Braintree.

=item CVV

Pass cvv to Braintree.

=back

Evan Brown (GH #7):

Add support for post authorization action.

=head1 LICENSE AND COPYRIGHT

Copyright 2011-2014 Stefan Hornburg (Racke).

This program is free software; you can redistribute it and/or modify it
under the terms of either: the GNU General Public License as published
by the Free Software Foundation; or the Artistic License.

See http://dev.perl.org/licenses/ for more information.

=head1 SEE ALSO

L<Net::Braintree>

=cut

1; # End of Business::OnlinePayment::Braintree