package Business::OnlineThirdPartyPayment;

use strict;
use vars qw($VERSION);
use Carp;
use base qw(Business::OnlinePayment);

require 5.005;

$VERSION = '0.10';
$VERSION = eval $VERSION; # modperlstyle: convert the string into a number

my %fields = (
    is_success          => undef,
    token               => undef,
    redirect            => undef,
    post_params         => undef,
    statustext          => undef,
    order_number        => undef,
    authorization       => undef,
    error_message       => undef,
    test_transaction    => undef,
    return_url          => undef,
    cancel_url          => undef,
);

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

    croak("unspecified processor") unless $processor;

    my $subclass = "${class}::$processor";
    eval "use $subclass";
    croak("unknown processor $processor ($@)") if $@;

    my $self = bless {processor => $processor}, $subclass;
    $self->build_subs(keys %fields);

    if($self->can("set_defaults")) {
        $self->set_defaults(%data);
    }

    foreach(keys %data) {
        my $key = lc($_);
        my $value = $data{$_};
        $key =~ s/^\-+//;
        $self->build_subs($key);
        $self->$key($value);
    }

    return $self;
}

1;

__END__

=head1 NAME

Business::OnlineThirdPartyPayment - Common interface to browser-based 
online payment processors

=head1 SYNOPSIS

  use Business::OnlineThirdPartyPayment;
  use CGI;

  my %processor_info = (
    'return_url'  => 'https://www.example.com/payment/return',
    'cancel_url'  => 'https://www.example.com/payment/cancel',
    # ... other processor-specific values
  );

  my $txn = Business::OnlineThirdPartyPayment->new('MyBank', %processor_info);
  # start a payment
  $txn->create( customer_id     => 4030,
                invoice_number  => 10318,
                amount          => 49.95,
                currency        => 'CAD',
                description     => 'Internet Services',
              );

  if ( $txn->is_success} ) {
    store_payment_id($txn->token); # keep it somewhere
    print CGI->redirect( $txn->redirect );
  } else {
    die $txn->error_message;
  }

  ...
  # then, at /payment/return...
  my $txn = Business::OnlineThirdPartyPayment->new('MyBank', %processor_info);
  $txn->token( get_payment_id($cgi) ); # probably from a session cookie
  my %params = $cgi->Vars;

  $txn->execute(%params);
  
  if ( $txn->is_success ) {
    store_payment_success($txn->order_number, $txn->authorization);
    print "Your payment was successful!\n";
  } else {
    store_payment_failure($txn->order_number, $txn->error_message);
    print "Your payment failed.\n";
  }

=head1 DESCRIPTION

Business::OnlineThirdPartyPayment is a generic module for processing payments
through online credit card processors, electronic cash systems, etc. through
which the payer's web browser is redirected.

=head1 METHODS

=head2 new($module, %processor_options)

Class method.  Create a Business::OnlineThirdPartyPayment object.
$module is required, and defines the gateway module to use.  The 
other options are processor-specific, but should always include:

=over 4

=item return_url - The callback URL to redirect the user to after completing 
a payment.

=item cancel_url - The URL to redirect the user to if they choose not to 
complete the payment.

=back

=head2 create(%content)

Tell the gateway module to start a transaction.  This may involve 
communicating with the gateway, or it may happen entirely locally.
%content is a hash containing some of the following:

=head3 TRANSACTION FIELDS

=over 4

=item amount - The amount of the transaction, as a decimal number. Required.

=item description - A description of the purchase. Required.

=item invoice_number - Your invoice number, if this payment is associated
with a specific invoice.

=item currency - Currency, specified as an ISO 4217 three-letter code, such 
as USD, CAD, EUR, AUD, DKK, GBP, JPY, NZD, etc.

=item customer_id - The customer number, if any.

=item email - Customer's email address.

=item customer_ip - IP address from which the transaction originated.

=back

For additional fields, see L<Business::OnlinePayment> and the specific
processor module used.

C<create()> sets properties of the transaction object to indicate the 
result.  These will always include:

=over 4

=item is_success - 1 if the transaction was created successfully, 0 
otherwise.

=item error_message - a text description of any error that occurred.
Since the payer hasn't provided a payment account or any other 
information at this stage, this is probably not meaningful to them.

=item token - a reference string that will be used to identify
this transaction later.  If is_success is true, you I<must> store this 
value in your merchant system and pass it to execute() later.  Merchant 
systems should allow at least 256 characters for this string.

=item statustext - a freeform text field for any state information 
that the gateway needs to receive to complete the transaction.

=item redirect - a URL to redirect the payer to.

=item post_params - the content of an HTTP POST request to send to the 
URL.  Since HTTP redirects can't include POST content, in this case the 
front-end system must provide another way (a self-submitting form) to 
induce the purchaser's browser to make the request.

=back

=head2 execute(%params)

Complete the transaction.  This should be called from the web server
(from 'return_url').  %params must contain any query parameters passed 
in the redirect, and token should be set on the object.

execute() will set the transaction fields to indicate the result:

=over 4

=item is_success: 1 if the payment was successful, 0 if it was not.

=item error_message: An error message describing the reason for failure.
Unlike the message returned from C<create()>, this probably is meaningful
to the user, though the gateway may already have shown it to them.

=item order_number: The transaction ID number assigned by the processor.

=item authorization: The credit card or other authorization number 
returned by the gateway.  This may be needed to refund the payment or 
for other customer service issues.

=back

If C<is_success> is true, the merchant system should record this as 
a successful payment and apply it to the customer's account.

=head1 AUTHOR

Mark Wells <mark@freeside.biz>, based in part on code by Jeff Finucane.

=head1 COPYRIGHT

Copyright (c) 1999-2004 Jason Kohles.
Copyright (c) 2007-2013 Freeside Internet Services, Inc.

All rights reserved. This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.

=head1 DISCLAIMER

THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

=head1 SEE ALSO

L<Business::OnlinePayment>, http://420.am/business-onlinepayment/

=cut
