#################### 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;