package WebSphere::Payment;

use HTTP::Headers;
use HTTP::Request;
use LWP::UserAgent;
use strict;
use vars qw($VERSION $defaultpmurl $defaultcurrency $ua $h $defaultpmadmin $defaultgwhost
$defaultgwport $defaultsetprof $defaultsignbrand $defaultaccountnumber $defaultcassette 
$defaultmerole $defaultpmhost $defaultaccounttitle);
$VERSION = '1.21';


#Default Values
$defaultpmadmin  = 'YWRtaW46YWRtaW4=';  #('user:password' base64 encoded)
$defaultpmurl = 'http://localhost/webapp/PaymentManager/PaymentServlet';
$defaultpmhost = 'localhost';
$defaultmerole = 2; # Supervisor
$defaultgwhost = '200.3.3.142';
$defaultgwport = '10010'; 
$defaultsetprof = 8;
$defaultsignbrand = 'VISA';
$defaultaccountnumber = 1000;
$defaultcassette = 'SET';
$defaultcurrency = 862;
$defaultaccounttitle = 'Default Account';

#
# Public Methods
#
#-------------------------------------------------------
# Subroutine:  new
# Author: Luis Moreno
# Date: 20010612
# Modified: 
# Description:
#-------------------------------------------------------
sub new() {
    my ($type, $class, $timeout, $pmhost) = ();
    my $defaulttimeout = 120;
    my $self = {};
    ($ua, $h) = ();

    $type = shift;
    $class = ref($type) || $type;
    bless($self, $class);

    $self->{pmurl} = shift || $defaultpmurl;
    $self->{pmadmin} = shift || $defaultpmadmin;
    $self->{currency} = shift || $defaultcurrency;
    $self->{prirc} = -2;
    $self->{secrc} = -2;
    $timeout = shift || $defaulttimeout;
    $ua = LWP::UserAgent->new; 
    $ua->timeout($timeout);
    $h = new HTTP::Headers; 
    $h->header(Connection => 'Keep-Alive',
	       Accept => 'application/XML',
	       Accept_Language => 'en-US',
	       Authorization => 'Basic ' . $self->{pmadmin},
	       Content_Type => 'application/x-www-form-urlencoded',
	       Host => 'localhost',
	       User_Agent => 'Java PaymentServerClient',
	       Content_Encoding => '8859_1');
    return $self;
}




#-------------------------------------------------------
# Subroutine:  acceptPayment
# Author: Luis Moreno
# Date: 20010612
# Modified:
# Description: Send a transaction using the Payment Manager engine, with the data 
# specified in the hash referenced by paydataref
#-------------------------------------------------------
sub acceptPayment() {
    my ($answer, $postcontent, $request, $inittime, $response, $deltatime) = ();
    my $self = shift;
    my $paydataref = shift;

    if (ref($paydataref) eq "HASH") {
		$paydataref->{operation} = 'ACCEPTPAYMENT';
		$paydataref->{paymenttype} = 'SET';
		$paydataref->{etapiversion} = 3;
		$paydataref->{currency} = $self->{currency} if (! $paydataref->{currency});
		$paydataref->{paymentamount} = $paydataref->{amount} if (! $paydataref->{paymentamount});
		$paydataref->{paymentnumber} = 1 if (! $paydataref->{paymentnumber});

		$postcontent = hash2content($paydataref);
		$h->content_length(length ($postcontent));
		$request = HTTP::Request->new(POST => $self->{pmurl},$h);
		$request->content($postcontent);
    
		$inittime = time;
		$response = $ua->request($request);
		$deltatime = time - $inittime;

		if ($response->is_success) {
		    $response->content =~ /<PSApiResult\sprimaryRC="(\d*)"\ssecondaryRC="(\d*)"\sobjectCount="\d*"\s*(\w*Message="(.*)\.")??\/>$/;
		    $self->{prirc} = $1;
		    $self->{secrc} = $2;
		    $self->{pmmessage} = $4;
		    $answer = 1;
		} else {
		    $self->{error}  = $response->message;
		}
		undef $request;
		undef $response;
    }   
    else {
	$self->{error} = "Invalid payment data reference"
    } 
    return($answer);
}

#-------------------------------------------------------
# Subroutine:  batchClose
# Author: Luis Moreno
# Date: 20010926
# Modified:
# Description: Closes a batch given the merchantnumber within a hash reference and the 
# number of the batch to be closed 
#-------------------------------------------------------
sub batchClose() {
    my ($answer, $postcontent, $request, $inittime, $response, $deltatime) = ();
    my $self = shift;
    my $paydataref = shift;
    my $batchnumber = shift;

    if (ref($paydataref) eq "HASH" and ($batchnumber >= 1)) {
		$paydataref->{operation} = 'BatchClose';
		$paydataref->{force} = 1;
		$paydataref->{etapiversion} = 3;
		$paydataref->{batchnumber} = $batchnumber;

		$postcontent = hash2content($paydataref);
		$h->content_length(length ($postcontent));
		$request = HTTP::Request->new(POST => $self->{pmurl},$h);
		$request->content($postcontent);
    
		$inittime = time;
		$response = $ua->request($request);
		$deltatime = time - $inittime;

		if ($response->is_success) {
		    $response->content =~ /<PSApiResult\sprimaryRC="(\d*)"\ssecondaryRC="(\d*)"\sobjectCount="\d*"\s*(\w*Message="(.*)\.")??\/>$/;
		    $self->{prirc} = $1;
		    $self->{secrc} = $2;
		    $self->{pmmessage} = $4;
		    $answer = 1;
		} else {
		    $self->{error}  = $response->message;
		}
		undef $request;
		undef $response;
    }   
    else {
	$self->{error} = "Invalid payment data reference";
    } 
    return($answer);
}

#-------------------------------------------------------
# Subroutine:  getOpenBatchNumber
# Author: Luis Moreno
# Date: 20010926
# Modified:
# Description: Given the merchantnumber within a hash reference returns the number of the 
# batch if exists 
#-------------------------------------------------------
sub getOpenBatchNumber() {
    my ($answer, $postcontent, $request, $inittime, $response, $deltatime) = ();
    my $self = shift;
    my $paydataref = shift;

    if (ref($paydataref) eq "HASH") {
	$paydataref->{operation} = 'QueryBatches';
	$paydataref->{state} = 'batch_open';
	$paydataref->{accountnumber} = $defaultaccountnumber
	    if (! $paydataref->{accountnumber});
	$paydataref->{etapiversion} = 3;

	$postcontent = hash2content($paydataref);
	$h->content_length(length ($postcontent));
	$request = HTTP::Request->new(POST => $self->{pmurl},$h);
	$request->content($postcontent);

	$inittime = time;
	$response = $ua->request($request);
	$deltatime = time - $inittime;

	if ($response->is_success) {
	    $response->content =~ /<PSApiResult\sprimaryRC="(\d*)"\ssecondaryRC="(\d*)"\sobjectCount="(\d*)"/;
	    $self->{prirc} = $1;
	    $self->{secrc} = $2;
	    if ($3 >= 1) {
		$answer = 1;
		$response->content =~ /batchNumber="(\d*)"/;
		$answer = $1;
	    }
	} else {
	    $self->{error}  = $response->message;
	}
	undef $request;
	undef $response;
    }
    else {
        $self->{error} = "Invalid payment data reference";
    }
    return($answer);
}

#-------------------------------------------------------
# Subroutine:  createMerchant
# Author: Luis Moreno
# Date: 20010926
# Modified:
# Description: Create a new merchant in the payment manager system 
#-------------------------------------------------------
sub createMerchant() {
    my ($answer, $postcontent, $request, $inittime, $response, $deltatime) = ();
    my $self = shift;
    my $paydataref = shift;

    if (ref($paydataref) eq "HASH") {
	$paydataref->{operation} = 'CreateMerchant';
	$paydataref->{etapiversion} = 3;
	$paydataref->{enabled} = 1;

	$postcontent = hash2content($paydataref);
	$h->content_length(length ($postcontent));
	$request = HTTP::Request->new(POST => $self->{pmurl},$h);
	$request->content($postcontent);
	$inittime = time;
	$response = $ua->request($request);
	$deltatime = time - $inittime;


	if ($response->is_success) {
	    $response->content =~ /<PSApiResult\sprimaryRC="(\d*)"\ssecondaryRC="(\d*)"\sobjectCount="\d*"\s*(\w*Message="(.*)\.")??\/>$/;
	    $self->{prirc} = $1;
	    $self->{secrc} = $2;
	    $self->{pmmessage} = $4;
	    $answer = 1;
	} else {
	    $self->{error}  = $response->message;
	}
	undef $request;
	undef $response;
    }   
    else {
	$self->{error} = "Invalid payment data reference";
    } 
    return($answer);
}

#-------------------------------------------------------
# Subroutine:  createPaySystem
# Author: Luis Moreno
# Date: 20010926
# Modified:
# Description: Authorize a merchant to use a pay system (SET for example). 
#-------------------------------------------------------
sub createPaySystem() {
    my ($answer, $postcontent, $request, $inittime, $response, $deltatime) = ();
    my $self = shift;
    my $paydataref = shift;

    if (ref($paydataref) eq "HASH") {
        $paydataref->{operation} = 'CreatePaySystem';
        $paydataref->{etapiversion} = 3;
        $paydataref->{enabled} = 1;

        $postcontent = hash2content($paydataref);
        $h->content_length(length ($postcontent));
        $request = HTTP::Request->new(POST => $self->{pmurl},$h);
        $request->content($postcontent);
        $inittime = time;
        $response = $ua->request($request);
        $deltatime = time - $inittime;


        if ($response->is_success) {
            $response->content =~ /<PSApiResult\sprimaryRC="(\d*)"\ssecondaryRC="(\d*)"\sobjectCount="\d*"\s*(\w*Message="(.*)\.")??\/>$/;
            $self->{prirc} = $1;
            $self->{secrc} = $2;
            $self->{pmmessage} = $4;
	    $answer = 1;
	}
	else {
	    $self->{error}  = $response->message;
	}
	undef $request;
	undef $response;
    }
    else {
	$self->{error} = "Invalid payment data reference";
    }
    return($answer);
}


#-------------------------------------------------------
# Subroutine:  createAccount
# Author: Luis Moreno
# Date: 20011001
# Modified:
# Description: Create a new Account for a merchant. An account represents the relationship 
# between a merchant and an acquirer
#-------------------------------------------------------
sub createAccount() {
    my ($answer, $postcontent, $request, $inittime, $response, $deltatime) = ();
    my $self = shift;
    my $paydataref = shift;

    if (ref($paydataref) eq "HASH") {
	$paydataref->{operation} = 'CreateAccount';
	$paydataref->{accountnumber} = $defaultaccountnumber
	    if (! $paydataref->{accountnumber});
	$paydataref->{'%24acquirersetprofile'} = $defaultsetprof
	    if (! $paydataref->{'%24acquiresetprofile'});
	$paydataref->{'%24gatewayhostname'} = $defaultgwhost
	    if (! $paydataref->{'%24gatewayhostname'});
	$paydataref->{'%24gatewayport'} = $defaultgwport
	    if (! $paydataref->{'%24gatewayport'});
	$paydataref->{'%24signingbrandid'} = $defaultsignbrand
	    if (! $paydataref->{'%24signingbrandid'});
	$paydataref->{'accounttitle'} = $defaultaccounttitle
	    if (! $paydataref->{'accounttitle'});
	$paydataref->{'financialinstitution'} = $defaultaccounttitle
	    if (! $paydataref->{'financialinstitution'});
        $paydataref->{etapiversion} = 3;
        $paydataref->{enabled} = 1;

        $postcontent = hash2content($paydataref);
        $h->content_length(length ($postcontent));
        $request = HTTP::Request->new(POST => $self->{pmurl},$h);
        $request->content($postcontent);
        $inittime = time;
        $response = $ua->request($request);
        $deltatime = time - $inittime;


        if ($response->is_success) {
            $response->content =~ /<PSApiResult\sprimaryRC="(\d*)"\ssecondaryRC="(\d*)"\sobjectCount="\d*"\s*(\w*Message="(.*)\.")??\/>$/;
            $self->{prirc} = $1;
            $self->{secrc} = $2;
            $self->{pmmessage} = $4;
	    $answer = 1;
        }
        else {
            $self->{error}  = $response->message;
        }
        undef $request;
        undef $response;
    }
    else {
        $self->{error} = "Invalid payment data reference";
    }
    return($answer);
}

#-------------------------------------------------------
# Subroutine:  SetUserAccessRights
# Author: Luis Moreno
# Date: 20011002
# Modified:
# Description: Set permissions to a user of the Realm. The user must have the same name 
# of the merchant 
#-------------------------------------------------------
sub setUserAccessRights() {
    my ($answer, $postcontent, $request, $inittime, $response, $deltatime) = ();
    my $self = shift;
    my $paydataref = shift;

    if (ref($paydataref) eq "HASH") {
        $paydataref->{operation} = 'SetUserAccessRights';
        $paydataref->{etapiversion} = 3;
        $paydataref->{user} = $paydataref->{merchanttitle};
	$paydataref->{role} = $defaultmerole
	    if (! $paydataref->{role});

        $postcontent = hash2content($paydataref);
        $h->content_length(length ($postcontent));
        $request = HTTP::Request->new(POST => $self->{pmurl},$h);
        $request->content($postcontent);
	$inittime = time;
	$response = $ua->request($request);
	$deltatime = time - $inittime;


	if ($response->is_success) {
            $response->content =~ /<PSApiResult\sprimaryRC="(\d*)"\ssecondaryRC="(\d*)"\sobjectCount="\d*"\s*(\w*Message="(.*)\.")??\/>$/;
            $self->{prirc} = $1;
            $self->{secrc} = $2;
            $self->{pmmessage} = $4;
            $answer = 1;

        }
	else {
	    $self->{error}  = $response->message;
	}
	undef $request;
	undef $response;
    }
    else {
	$self->{error} = "Invalid payment data reference";
    }
    return($answer);
}

#-------------------------------------------------------
# Subroutine:  close
# Author: Luis Moreno
# Date: 20010612
# Modified:
# Description:
#-------------------------------------------------------
sub close() {
    undef $ua;
    undef $h;
}

# Non public subs

#-------------------------------------------------------
# Subroutine:  hash2content
# Author: Luis Moreno
# Date: 20010612
# Modified:
# Description:
#-------------------------------------------------------
sub hash2content($hashref) {
    my ($hashref) = @_;
    my ($postcontent, $key) = ();

    foreach $key (keys %{$hashref}) {
	$postcontent .= uc($key) . '=' . $hashref->{$key} . '&';
    }
    chop($postcontent);
    return($postcontent);
}

1;

__END__

=head1 NAME

CashRegister - Simple Perl interface to IBM WebSphere
Payment Manager 2.2 API

=head1 SYNOPSIS

use WebSphere::Payment;
$cashregister = new WebSphere::Payment($pmurl,$currency,$admin, $timeout); 
# Creating a merchant
$paystubref = {merchantnumber => $merchantnumber,
               merchanttitle => $merchantname,
               cassettename => 'SET',
           };
my ($errortext) = ();

if ($cashregister->createMerchant($paystubref) and
    !($cashregister->{prirc} or $cashregister->{secrc})) {
    if ($cashregister->createPaySystem($paystubref) and
        !($cashregister->{prirc} or $cashregister->{secrc})) {
        if ($cashregister->createAccount($paystubref) and
            !($cashregister->{prirc} or $cashregister->{secrc})) {
            if (! $cashregister->setUserAccessRights($paystubref) or
                ($cashregister->{prirc} or $cashregister->{secrc})) {
                $errortext =  "SetUserAccessRights error";
            }
        }
        else {
            $errortext =  "Create Account error";
        }
    }
    else {
        $errortext = "Create PaySystem error";
    }
}
else {
    $errortext = "Create Merchant error";
}
if ($errortext) {
    print "Fail...$errortext\n";
}
else {
    print "Results...OK\n";
}
print "prirc: " . $cashregister->{prirc} .  "\n";
print "secrc: " . $cashregister->{secrc} . "\n";
print "pmmessage: " . $cashregister->{pmmessage} . "\n";

$paystubref = {merchantnumber => $merchantnumber, 
	       ordernumber => $ordernumber, 
	       approveflag => 0, 
	       depositflag => 0, 
	       amount => $amount, 
	       '%24expiry' => $yyyydd, 
	       '%24pan' => $cardnumber, 
	       '%24brand' => $cardtype, 
	       '%24orderdescription' => $description,
	       '%24cardverifycodes' => $cvv2};

# Sending a transaction
if ($cashregister->acceptPayment($paystubref)) { 
    print "Results\n"; 
    print "prirc: " . $cashregister->{prirc} . "\n";
    print "secrc: " . $cashregister->{secrc} . "\n";
}
print "pmmessage: " . $cashregister->{pmmessage} . "\n"; 
# Closing a Batch
if ($batch = $cashregister->getOpenBatchNumber($paystubref)) {
    print "\n\nClosing the Batch number: $batch\n";
    $cashregister->batchClose($paystubref, $batch);
    print "prirc: " . $cashregister->{prirc} .  "\n";
    print "secrc: " . $cashregister->{secrc} . "\n";
    print "pmmessage: " . $cashregister->{pmmessage} . "\n";
}
else {
    print "No batch opens. " . $cashregister->{error};
}
$cashregister->close();

=head1 REQUIRES

Perl5.005

=head1 EXPORTS
Nothing

=head1 DESCRIPTION

WebSphere::Payment provides a simple Interface to the
API of the payment engine IBM WebSphere Payment
Manager 2.2. It achieves this task, through commands
send via http POST method.

=head1 METHODS


=head2 Creation

=over 4

=item new WebSphere::Payment($pmurl,$admin,$currency,$timeout)

Create a new WebSphere::Payment object. If no parameters
are specified, the object will be initialized with
the default values. The pmurl is the url where the
Payment Manager servlet (PaymentServlet) is listening.
The currency parameter, represent the type of currency
the transactions will use. The admin parameter
consists of a userid and password string, separated
by a single colon (":") character, encoded with a
base64 encoding. This userid must have administrator
permissions on the Payment Manager.


=back

=head2 API Methods

=over 4

=item acceptPayment($paydataref)

Send a transaction using the Payment Manager engine,
with the data specified in the hash referenced by
paydataref.

=item batchClose($paydataref, batchnumber)

Closes a batch given the merchantnumber within a hash reference and
the number of the batch to be closed

=item getOpenBatchNumber($paydataref)

Given the merchantnumber within a hash reference returns the number of the batch if exists

=item createMerchant($paydataref)

Create a new merchant in the payment manager system

=item createPaySystem($paydataref)

Authorize a merchant to use a pay system (SET for example).

=item createAccount($paydataref)

Create a new Account for a merchant. An account represents the relationship between 
a merchant and an acquirer

=item SetUserAccessRights($paydataref)

Set permissions to a user of the Realm. The user must have the same name of the merchant 

=back

=head1 HISTORY

This module was originally created in Jun 2001 by Luis Moreno
1.20 Methods: batchClose, getOpenBatchNumber, createMerchant, createPaySystem, createAccount, setUserAccessRights (Oct 2001)
1.21 Minor documentation changes


=head1 AUTHOR

Luis Moreno, luis@cantv.net

=head1 COPYRIGHT

The IBM WebSphere Payment Manager is trademark of the
IBM Corporation in the United States or both.

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

=head1 SEE ALSO

perl(1), IBM WebSphere Payment Manager Programmer's Guide and Reference

=cut