#################### main pod documentation begin ###################
=head1 NAME
Zymonic::Action::PaymentGateway -s Zymonic Workflow System Action module
=head1 SYNOPSIS
use Zymonic::Action::PaymentGateway;
Performs a PaymentGateway authroisation using the Decryptor.
=head1 DESCRIPTION
Performs a PaymentGateway authroisation using the Decryptor.
=head1 USAGE
The following ClassOption Schema should be used:
Token should be a valid PTE token. Will be passed to decryptor as:
card_number => ENCRYPTEDtokenENCRYPTED
so when it reaches decrytpor it will have been decrytped.
Extras is used to pass extra values through to the PaymentGateway, e.g.
card_number, epiry_date, etc... or anything needed for a specific
PaymentGateway.
=head1 BUGS
NONE
=head1 SUPPORT
As in the license, Zymonic is provided without warranty or support
unless purchased separately, however... If you email
zymonic-support@zednax.com your issue will be noted and may
receive a response.
For security issues, please contact zymonic-security@zednax.com and
someone will respond within 8 working hours.
=head1 AUTHOR
Alex Masidlover et al.
CPAN ID: MODAUTHOR
Zednax Limited
alex.masidlover@zednax.com
http://www.zednax.com
=head1 COPYRIGHT
This program is free software licensed under the...
Zymonic Public License 1.0
The full text of the license can be found in the
LICENSE file included with this module.
Other licenses may be acceptable if including
parts of Zymonic in larger projects, please
contact Zednax for details.
=head1 SEE ALSO
Zymonic
perl(1).
=cut
#################### main pod documentation end ###################
package Zymonic::Action::PaymentGateway;
use strict;
BEGIN
{
use Exporter ();
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
$VERSION = 'D1-r7186';
@ISA = qw(Exporter);
#Give a hoot don't pollute, do not export more than needed by default
@EXPORT = qw();
@EXPORT_OK = qw();
%EXPORT_TAGS = ();
}
use base "Zymonic::Action";
use Zymonic::Decryptor::Client;
use Zymonic::Table;
use Zymonic::Utils qw(get_array debug deep_replace_card_numbers);
use Data::Dumper;
use Zymonic;
use Exception::Class (
'Zymonic::Exception::Decryptor' => {
isa => 'Zymonic::Exception',
fields => ['decryptor'],
description => 'Decryptor related exception',
},
'Zymonic::Exception::Decryptor::Action' => {
isa => 'Zymonic::Exception::Decryptor',
fields => ['decryptor'],
description => 'Decryptor related exception',
},
'Zymonic::Exception::Decryptor::Action::PaymentGateway' => {
isa => 'Zymonic::Exception::Decryptor::Action',
fields => ['decryptor'],
description => 'Decryptor related exception',
},
'Zymonic::Exception::Decryptor::Action::PaymentGateway::NoTokenField' => {
isa => 'Zymonic::Exception::Decryptor::Action::PaymentGateway',
fields => ['zname'],
description => 'Unable to find Token field',
},
'Zymonic::Exception::Decryptor::Action::PaymentGateway::NoTransactionID' => {
isa => 'Zymonic::Exception::Decryptor::Action::PaymentGateway',
fields => ['zname'],
description => 'Unable to find TransactionID field',
},
'Zymonic::Exception::Decryptor::Action::PaymentGateway::NoResponse' => {
isa => 'Zymonic::Exception::Decryptor::Action::PaymentGateway',
fields => ['zname'],
description => 'No response received from decryptor',
},
'Zymonic::Exception::Decryptor::Action::PaymentGateway::DecryptorError' => {
isa => 'Zymonic::Exception::Decryptor::Action::PaymentGateway',
fields => [ 'zname', 'decryptor_error' ],
description => 'Error from decryptor: [[decryptor_error]]',
},
'Zymonic::Exception::Decryptor::Action::PaymentGateway::NoResponseCode' => {
isa => 'Zymonic::Exception::Decryptor::Action::PaymentGateway',
fields => ['zname'],
description => 'Response from decryptor did not contain response code',
},
'Zymonic::Exception::Decryptor::Action::PaymentGateway::Lookup' => {
isa => 'Zymonic::Exception::Decryptor::Action::PaymentGateway',
fields => ['zname'],
description => 'Need Payment Gateway to look it up',
},
'Zymonic::Exception::Decryptor::Action::PaymentGateway::NeedTransactionID' => {
isa => 'Zymonic::Exception::Decryptor::Action::PaymentGateway',
fields => ['zname'],
description => 'Need Transaction ID to lookup transaction',
},
'Zymonic::Exception::Decryptor::Action::PaymentGateway::LookupPaymentID' => {
isa => 'Zymonic::Exception::Decryptor::Action::PaymentGateway',
fields => ['zname'],
description => 'Need token to lookup Payment Data',
},
'Zymonic::Exception::Decryptor::Action::PaymentGateway::ConnectionError' => {
isa => 'Zymonic::Exception::Decryptor::Action::PaymentGateway',
fields => [],
description => 'Error from decryptor: Connection Error',
},
);
################# subroutine header begin ####################
=head2 timeout
Usage : $action->timeout
Purpose : Gets the timeout value from class options
Returns : a scalar timeout value
Argument : nothing
Throws : nothing
Comment :
See Also :
=cut
#################### subroutine header end ####################
sub timeout
{
my $self = shift;
return $self->static_or_field_value( 'Timeout', 'optional' );
}
################# subroutine header begin ####################
=head2 payment_gateway
Usage : $action->payment_gateway
Purpose : Gets the payment_gateway value from the transaction settings.
Returns : a scalar payment_gateway value
Argument : nothing
Throws : nothing
Comment : Looks up transaciton, gets merchant id, then finds payment
gateway from that.
See Also :
=cut
#################### subroutine header end ####################
sub payment_gateway
{
my $self = shift;
return $self->static_or_field_value( 'PaymentGatway', 'optional' );
}
################# subroutine header begin ####################
=head2 token
Usage : $action->token
Purpose : Gets the token value from class option defined field
Returns : a scalar token value
Argument : nothing
Throws : nothing
Comment :
See Also :
=cut
#################### subroutine header end ####################
sub token
{
my $self = shift;
return $self->get_field_value( $self->{xmldef}->{ClassOptions}->{Token}->{Field}->{ZName}->{content} );
}
################# subroutine header begin ####################
=head2 transaction_id
Usage : $action->transaction_id
Purpose : Gets the transaction_id value from class option defined field
Returns : a scalar transaction_id value
Argument : nothing
Throws : nothing
Comment :
See Also :
=cut
#################### subroutine header end ####################
sub transaction_id
{
my $self = shift;
return $self->get_field_value( $self->{xmldef}->{ClassOptions}->{TransactionID}->{Field}->{ZName}->{content} );
}
################# subroutine header begin ####################
=head2 extras
Usage : $action->extras
Purpose : Gets the extras value from class options
Returns : A hashref of key/value options as retrieved from ClassOptions.
Argument : nothing
Throws : nothing
Comment :
See Also :
=cut
#################### subroutine header end ####################
sub extras
{
my $self = shift;
my $extras = {};
foreach my $extra ( get_array( $self->{ClassOptions}->{Extras} ) )
{
if ( $extra->{Key} && $extra->{Value} )
{
$extras->{ $extra->{Key}->{content} } = $self->static_or_field_value( 'Value', '', '', $extra );
}
}
return $extras;
}
#################### subroutine header begin ####################
=head2 do
Usage : $action->do
Purpose : Performs the action, i.e. generates the report
Returns : nothing
Argument : nothing
Throws : TODO
Comment : nothing
=cut
#################### subroutine header end ####################
sub do
{
my $self = shift;
my $fake = shift || '';
my $no_display_attributes = shift || 0;
my $result = $self->SUPER::do( $fake, $no_display_attributes );
if ( $self->{success} && $self->{success} eq 'true' )
{
my $token = $self->token();
my $transaction_id = $self->transaction_id();
my $payment_gateway = $self->payment_gateway();
my $request = {
messagetype => $self->message_type(),
payment_gateway_type => $payment_gateway,
%{ $self->payment_gateway_details($payment_gateway) },
%{ $self->payment_data_details($token) },
%{ $self->transaction_details($transaction_id) },
token => $token,
card_number => 'ENCRYPTED' . $token . 'ENCRYPTED',
csc => $self->csc($token),
timeout => $self->timeout || '',
%{ $self->extras }
};
# if card has expired, use default expiry data
my $card_exp_date = $request->{card_exp_date} || '';
if ( $card_exp_date and length( $request->{default_exp_date} ) > 0 )
{
my $card_exp_m = substr( $card_exp_date, 0, 2 );
my $card_exp_y = substr( $card_exp_date, 2, 2 );
my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday ) = $self->zymonic_time();
$mon += 1;
$year = substr( $year + 1900, 2, 2 );
if ( $card_exp_y < $year || ( $card_exp_y == $year && $card_exp_m < $mon ) )
{
debug("Card has expired ($card_exp_date), using default expiry date ($request->{default_exp_date})");
$request->{card_exp_date} = $request->{default_exp_date};
}
}
my $debug_request = { %{$request} };
$debug_request->{csc} = 'X' x length( $debug_request->{csc} )
if $debug_request->{csc} && $debug_request->{csc} =~ /^\d+$/;
debug( 'Payment Gateway request: ' . Dumper( deep_replace_card_numbers($debug_request) ) );
# call the decryptor to process the transactions through the gateway
$self->{response} = $self->decryptor_client()->call_decryptor($request);
debug( 'Payment Gateway response: ' . Dumper( deep_replace_card_numbers( $self->{response} ) ) );
Zymonic::Exception::Decryptor::Action::PaymentGateway::NoResponse->throw(
zname => $self->{zname},
catchable => 'false'
) unless ref( $self->{response} ) && keys %{ $self->{response} } > 0;
if ( $self->{response}->{error} )
{
if ( $self->{response}->{error} =~ /Connection Error/ )
{
Zymonic::Exception::Decryptor::Action::PaymentGateway::ConnectionError->throw( catchable => 'false', );
}
else
{
Zymonic::Exception::Decryptor::Action::PaymentGateway::DecryptorError->throw(
zname => $self->{zname},
catchable => 'false',
decryptor_error => $self->{response}->{error},
);
}
}
elsif ( $self->{response}->{response_code} )
{
$result->{success} = 'true';
}
else
{
Zymonic::Exception::Decryptor::Action::PaymentGateway::NoResponseCode->throw(
zname => $self->{zname},
catchable => 'false'
);
}
}
$self->{success} = $result->{success};
return $result;
}
#################### subroutine header begin ####################
=head2 decyptor_client
Usage : $form->decryptor_client;
Purpose : TODO
Returns : nothing
Argument : nothing
Throws : TODO
Comment :
See Also :
=cut
#################### subroutine header end ####################
sub decryptor_client
{
my $self = shift;
$self->{decryptor_client} =
Zymonic::Decryptor::Client->new( parent => $self, config => $self->{config}, db => $self->{DB} )
unless ( defined( $self->{decryptor_client} ) );
return $self->{decryptor_client};
}
#################### subroutine header begin ####################
=head2 value
Usage : $action->value($name)
Purpose : Returns a 'value' from an Action.
Returns : a scalar value
Argument : the name of the value to get
Throws :
Comment :
See Also :
=cut
#################### subroutine header end ####################
sub value
{
my $self = shift;
my $name = shift || '';
# get any values from response
return $self->{response}->{$name} if defined $self->{response}->{$name};
return $self->SUPER::value($name);
}
#################### subroutine header begin ####################
=head2 csc
Usage : $self->csc($token)
Purpose : Returns the csc to use.
Returns : value to use for csc.
Argument : A payment_data token
Throws : nothing
Comment :
See Also :
=cut
#################### subroutine header end ####################
sub csc
{
my $self = shift;
my $token = shift;
# no csc by default
return '';
}
#################### subroutine header begin ####################
=head2 payment_gateway_details
Usage : $self->payment_gateway_details($pg)
Purpose : Returns the settings from DB for the given payment gateway.
Returns : A hashref of settings for the specified payment gateway.
Argument : A payment gateway name.
Throws : Zymonic::Exception::Action
Comment :
See Also :
=cut
#################### subroutine header end ####################
sub payment_gateway_details
{
my $self = shift;
my $pg = shift;
Zymonic::Exception::Decryptor::Action::PaymentGateway::Lookup->throw(
zname => $self->{zname},
catchable => 'true'
) unless $pg;
# check in cache
return $self->{payment_gateway_details}->{$pg} if $self->{payment_gateway_details}->{$pg};
# load payment gateway table
my $payment_gateway_table = Zymonic::Table->new(
parent => $self,
zname => 'pte_payment_gateways',
config => $self->{config},
auth => $self->{auth},
DB => $self->{DB},
filter => {
where => 'pg_name = ?' . ( $self->{payment_gateway_id} ? ' AND payment_gateways.id = ?' : '' ),
params => [ $pg, ( $self->{payment_gateway_id} ? ( $self->{payment_gateway_id} ) : () ) ]
}
);
$self->{payment_gateway_details}->{$pg} = $payment_gateway_table->simple_get_all_records()->[0];
# set payment_gateway_id
$self->{payment_gateway_details}->{$pg}->{payment_gateway_id} = $self->{payment_gateway_details}->{$pg}->{id};
return $self->{payment_gateway_details}->{$pg};
}
#################### subroutine header begin ####################
=head2 transaction_details
Usage : $self->transaction_details($id)
Purpose : Returns the settings from DB for the given transaction id.
Returns : A hashref of settings for the specified transaction.
Argument : A transaction id
Throws : nothing
Comment :
See Also :
=cut
#################### subroutine header end ####################
sub transaction_details
{
my $self = shift;
my $id = shift;
Zymonic::Exception::Decryptor::Action::PaymentGateway::NeedTransactionID->throw(
zname => $self->{zname},
catchable => 'true'
) unless $id;
# check in cache
return $self->{transaction_details}->{$id} if $self->{transaction_details}->{$id};
# load transactions table
my $transaction_table = Zymonic::Table->new(
parent => $self,
zname => 'pte_tns',
config => $self->{config},
auth => $self->{auth},
DB => $self->{DB},
);
$self->{transaction_details}->{$id} = $transaction_table->simple_get_all_records(
{
where_clause => $transaction_table->{tableasname} . '.id = ?',
where_params => [$id]
}
)->[0];
# get country from MID
my $mid_table = Zymonic::Table->new(
parent => $self,
zname => 'pte_mids',
config => $self->{config},
auth => $self->{auth},
DB => $self->{DB},
);
$mid_table->get_all_records(
{
where_clause => $mid_table->{tableasname} . '.mid = ?',
where_params => [ $self->{transaction_details}->{$id}->{mid} ]
}
);
$self->{transaction_details}->{$id}->{country} =
$mid_table->get_field_value( 'pte_mids_country', $mid_table->{records}->[0] );
# if currency not set on transaction get it from MID
unless ( $self->{transaction_details}->{$id}->{currency} )
{
$self->{transaction_details}->{$id}->{currency} =
$mid_table->get_field_value( 'pte_mids_currency', $mid_table->{records}->[0] );
}
# set transaction_id
$self->{transaction_details}->{$id}->{transaction_id} = $id;
return $self->{transaction_details}->{$id};
}
#################### subroutine header begin ####################
=head2 payment_data_details
Usage : $self->payment_data_details($token)
Purpose : Returns the settings from DB for the given payment_data token.
Returns : A hashref of settings for the specified payment_data.
Argument : A payment_data token
Throws : nothing
Comment :
See Also :
=cut
#################### subroutine header end ####################
sub payment_data_details
{
my $self = shift;
my $token = shift;
Zymonic::Exception::Decryptor::Action::PaymentGateway::LookupPaymentID->throw(
zname => $self->{zname},
catchable => 'true'
) unless $token;
# check in cache
return $self->{payment_data_details}->{$token} if $self->{payment_data_details}->{$token};
# load payment data table
my $payment_data_table = Zymonic::Table->new(
parent => $self,
zname => 'pte_cards',
config => $self->{config},
auth => $self->{auth},
DB => $self->{DB},
filter => { where => 'token = ?', params => [$token] }
);
$self->{payment_data_details}->{$token} = $payment_data_table->simple_get_all_records()->[0];
# set payment_data_id
$self->{payment_data_details}->{$token}->{payment_data_id} = $self->{payment_data_details}->{$token}->{id};
return $self->{payment_data_details}->{$token};
}
################# subroutine header begin ####################
=head2 message_type
Usage : $action->message_type
Purpose : The message type to use in the decryptor call
Returns : s string
Argument : nothing
Throws : nothing
Comment :
See Also :
=cut
#################### subroutine header end ####################
sub message_type
{
my $self = shift;
return 'PaymentGateway';
}
1;