#################### 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... Alfresco 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; #################### subroutine header begin #################### =head2 init Usage : N/A Purpose : The init method is called by the object constructor to initialise the object fields. In this case it retrieves the form definition from the config module and then loads all the fields as objects. Returns : nothing Argument : nothing Throws : nothing Comment : Suggested that the Transition and all the mechanisms called by Action to have permission checks that there is no need for action to have permissions on it at this stage. See Also : =cut #################### subroutine header end #################### sub init { my $self = shift; $self->{other_fields} = {}; if ( $self->{state} && $self->{state}->{form} ) { $self->{other_fields} = $self->{state}->{form}->{records}->[0]->{fields}; } } ################# 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', $self->{other_fields}, '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', $self->{other_fields}, '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->{token_field}->value if $self->{token_field}; my $tf_ref = $self->{other_fields}->{ $self->{xmldef}->{ClassOptions}->{Token}->{Field}->{ZName}->{content} }; Zymonic::Exception::Action->throw( error => "Unable to find Token field: " . $self->{xmldef}->{ClassOptions}->{TokenID}->{Field}->{ZName}->{content}, zname => $self->{zname}, catchable => 'false' ) unless $tf_ref; $self->{token_field} = $tf_ref->{parent}->get_object($tf_ref); $self->{token_field}->value( $self->{token_field}->raw_value, $self->{token_field}->{fromdb} ) unless defined $self->{token_field}->value; return $self->{token_field}->value; } ################# 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->{transaction_id_field}->value if $self->{transaction_id_field}; my $ti_ref = $self->{other_fields}->{ $self->{xmldef}->{ClassOptions}->{TransactionID}->{Field}->{ZName}->{content} }; Zymonic::Exception::Action->throw( error => "Unable to find TransactionID field: " . $self->{xmldef}->{ClassOptions}->{TransactionID}->{Field}->{ZName}->{content} . ' in (' . join( ', ', map { $_->{zname} } get_array( $self->{other_fields} ) ) . ')', zname => $self->{zname}, catchable => 'false' ) unless $ti_ref; $self->{transaction_id_field} = $ti_ref->{parent}->get_object($ti_ref); $self->{transaction_id_field} ->value( $self->{transaction_id_field}->raw_value, $self->{transaction_id_field}->{fromdb} ) unless defined $self->{transaction_id_field}->value; return $self->{transaction_id_field}->value; } ################# 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', $self->{other_fields}, '', '', $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 $conditions = $self->conditions($fake); $self->{condition_passed} = 'false'; $self->{success} = 'false'; if ( $conditions->{condition_passed} ) { $self->{condition_passed} = 'true'; my $token = $self->token(); my $transaction_id = $self->transaction_id(); my $payment_gateway = $self->payment_gateway(); my $request = { messagetype => 'PaymentGateway', payment_gateway_type => $payment_gateway, %{ $self->payment_gateway_details($payment_gateway) }, %{ $self->transaction_details($transaction_id) }, %{ $self->payment_data_details($token) }, token => $token, card_number => 'ENCRYPTED' . $token . 'ENCRYPTED', timeout => $self->timeout || '', %{ $self->extras } }; debug( 'Payment Gateway request: ' . Dumper( deep_replace_card_numbers($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::Action->throw( error => 'No response received from decryptor', zname => $self->{zname}, catchable => 'false' ) unless ref( $self->{response} ) && keys %{ $self->{response} } > 0; if ( $self->{response}->{error} ) { Zymonic::Exception::Action->throw( error => 'Error from decryptor: ' . $self->{response}->{error}, zname => $self->{zname}, catchable => 'false' ); } else { $self->{success} = 'true'; } } } #################### 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 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::Action->throw( zname => $self->{zname}, error => 'Need Payment Gateway to look it up', 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 = ?', params => [$pg] } ); $payment_gateway_table->get_all_records(); # map into simple hash and add to cache $self->{payment_gateway_details}->{$pg} = { map { $_->value( $_->raw_value, $_->{fromdb} ) unless defined $_->value; $_->{field} => $_->value } map { $payment_gateway_table->get_object($_) } values( %{ $payment_gateway_table->{records}->[0]->{fields} } ) }; # 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::Action->throw( zname => $self->{zname}, error => 'Need Transaction ID to lookup transaction', 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}, ); $transaction_table->get_all_records( { where_clause => $transaction_table->{tableasname} . '.id = ?', where_params => [$id] } ); # map into simple hash and add to cache $self->{transaction_details}->{$id} = { map { $_->value( $_->raw_value, $_->{fromdb} ) unless defined $_->value; $_->{field} => $_->value } map { $transaction_table->get_object($_) } values( %{ $transaction_table->{records}->[0]->{fields} } ) }; # grab csc from form (not in table) my $csc_ref = $self->{other_fields}->{CSC_tef}; my $csc_field = $csc_ref->{parent}->get_object($csc_ref) if $csc_ref; if ($csc_field) { $csc_field->value( $csc_field->raw_value, $csc_field->{fromdb} ) unless defined $csc_field->value; $self->{transaction_details}->{$id}->{csc} = $csc_field->value; } # 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} ] } ); my $cnf_ref = $mid_table->{records}->[0]->{fields}->{pte_mids_country}; my $cnf_field = $mid_table->get_object($cnf_ref) if $cnf_ref; if ($cnf_field) { $cnf_field->value( $cnf_field->raw_value, $cnf_field->{fromdb} ) unless defined $cnf_field->value; $self->{transaction_details}->{$id}->{country} = $cnf_field->value; } # if currency not set on transaction get it from MID unless ( $self->{transaction_details}->{$id}->{currency} ) { my $crf_ref = $mid_table->{records}->[0]->{fields}->{pte_mids_currency}; my $crf_field = $mid_table->get_object($crf_ref) if $crf_ref; if ($crf_field) { $crf_field->value( $crf_field->raw_value, $crf_field->{fromdb} ) unless defined $crf_field->value; $self->{transaction_details}->{$id}->{currency} = $crf_field->value; } } # 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::Action->throw( zname => $self->{zname}, error => 'Need token to lookup Payment Data', 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] } ); $payment_data_table->get_all_records(); # map into simple hash and add to cache $self->{payment_data_details}->{$token} = { map { $_->value( $_->raw_value, $_->{fromdb} ) unless defined $_->value; $_->{field} => $_->value } map { $payment_data_table->get_object($_) } values( %{ $payment_data_table->{records}->[0]->{fields} } ) }; # set payment_data_id $self->{payment_data_details}->{$token}->{payment_data_id} = $self->{payment_data_details}->{$token}->{id}; return $self->{payment_data_details}->{$token}; } 1;