1146576; $::sPlugInScriptError = ""; # used to return error message if any my ($sStatus, $sMessage) = ProcessAuthoriseCallBack(); # do the post authorisation if ($sStatus != $::SUCCESS) { $::sPlugInScriptError = $sMessage; # error will be logged to error.err by calling method } return ($::SUCCESS); sub ValidateHashResult { LogData("ValidateHashResult: Validating NMI HASH"); my $pInputMap = shift; my $sPostData; my ($sName, $sValue); while (($sName, $sValue) = each (%$pInputMap)) { if ($sName =~ /^ekashu_/) { $sPostData .= ACTINIC::EncodeText2($sName, $::FALSE) . "=" . ACTINIC::EncodeText2($sValue, $::FALSE) . "&"; } } my $sEkashuHost = (1 == $::g_InputHash{TM}) ? "test.ekashu.com" : "live.ekashu.com"; my $SSLConnection = SSLConnection->new($sEkashuHost, 443, "/validate_hash_code.php"); $SSLConnection->SetRequestMethod("POST"); $SSLConnection->SetHeaderValue("Content-Type", "application/x-www-form-urlencoded"); $SSLConnection->SendRequest($sPostData); # # Due to the way the error handling was done connection status # also means not 200 OK so we need to check the response code first # if ($SSLConnection->GetResponseCode() != 200) { return $::FALSE; } return $::TRUE; } sub ProcessAuthoriseCallBack { $::g_sActPayStyleSheetURL = 'https://online.sellerdeckpayments.com/hosted/live/sellerdeck/ekashu.css'; if ($::g_InputHash{'ACTION'} !~ m/^OFFLINE_AUTHORIZE_/i) # if we're not doing an offline order { eval 'if ($sADF09 ne "") { $::g_sActPayStyleSheetURL = $sADF09; }'; # substitute a custom stylesheet if supplied } my ($status, $sError, $pmapInputNameToValue, $sPostedData) = ReadPostData(); if ($status != $::SUCCESS) { # # Send the error message to PSP # ACTINIC::PrintPSPResponse('text/plain', $sError, '500 Internal Server Error'); # tell PSP the request failed and why return ($::FAILURE, $sError); } if (($::g_InputHash{'NOAUTH'} ne "1") && # if not receipt call back (0 == scalar keys %$pmapInputNameToValue)) # if the POST data is empty, it was already read { $sError = "No POSTED data received."; ACTINIC::PrintPSPResponse('text/plain', $sError, '500 Internal Server Error'); # tell PSP the request failed and why return ($::FAILURE, $sError); } if ((0 < length $sPostedData) || ('1' ne $::g_InputHash{'NOAUTH'})) { # # Call validation only if this is an authorisation call back or # it is the MOTO success/failure request and we have input data # if (!ValidateHashResult($pmapInputNameToValue)) { $sError = "Hash code result validation failed."; ACTINIC::PrintPSPResponse('text/plain', $sError, '500 Internal Server Error'); # tell PSP the request failed and why return ($::FAILURE, $sError); } } # # Determine if this was a successful transaction # if ($::g_InputHash{'NOAUTH'} eq "1") { # # Whatever is printed here is displayed directly to the buyer # if ($::g_InputHash{'ERROR'} eq "1") { # # Transaction was not a success, display error page and end # @Response = DisplayOCCErrorResponsePage(); } else { # # Transaction was a success, immediately display success HTML # @Response = DisplayOCCSuccessResponsePage(); } if ($Response[0] != $::SUCCESS) { # # Tell buyer we couldn't handle the request # ACTINIC::PrintText($Response[1]); return ($::FAILURE, $Response[1]); } else { # # Tell the buyer the reult of the transaction # ACTINIC::PrintPage($Response[2], undef, $::TRUE, undef, $::TRUE); } # # Nothing else to do here # return ($::SUCCESS); } # # If we are here then it is an authorization callback # First of all check if we need to record authorization at all ( i.e. it's not a failure) # if ($pmapInputNameToValue->{'ekashu_auth_result'} ne 'success') { ACTINIC::PrintPSPResponse('text/plain', "OK-" . $pmapInputNameToValue->{'ekashu_auth_result'}); return ($::SUCCESS); } # # Ok so now we know we have a transaction so we'll cherry-pick the bits we want # my $sActinicFormatOriginalData = 'PATH=' . $::sPath; # # Add the order number # $sActinicFormatOriginalData .= '&ON=' . $::g_InputHash{ON}; # # Add the amount # # Assume that it is only £ and hence the multiplier is 100 for the currency # we *100 because the order processing will attempt to calculate amount # from the currency divider (100) for £ $sActinicFormatOriginalData .= '&AM=' . $pmapInputNameToValue->{'ekashu_amount'} * 100; # # Next line is hidden value??? don't know why it is in all postprocessing scripts # $sActinicFormatOriginalData .= '&x_amount=' . $pmapInputNameToValue->{'ekashu_amount'} * 100; # # Add the transaction_id # $sActinicFormatOriginalData .= '&CD=' . $pmapInputNameToValue->{'ekashu_auth_code'}; # # Get the current date/time on the server # my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst, $sDate); ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = gmtime($pmapInputNameToValue->{'ekashu_date_time_local'}); # platform independent time $mon++; # make month 1 based $year += 1900; # make year AD based $sDate = sprintf("%4.4d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d", $year, $mon, $mday, $hour, $min, $sec); ($sDate) = ACTINIC::EncodeText2($sDate, $::FALSE); # # Add the transaction date # $sActinicFormatOriginalData .= '&DT=' . $sDate; # # Add the test mode flag if supplied # if(defined $::g_InputHash{TM}) { $sActinicFormatOriginalData .= '&TM=' . $::g_InputHash{TM}; } # # Add the authorization only flag if supplied # if(defined $::g_InputHash{PA}) { $sActinicFormatOriginalData .= '&PA=' . $::g_InputHash{PA}; } # # Add the transaction ID # # This transaction ID is displayed in the Actinic UI - Use the eKashu-genererated ID # $sActinicFormatOriginalData .= '&TX=' . $pmapInputNameToValue->{'ekashu_transaction_id'}; # # This transaction ID is used for generating the signature so we must use the transaction # ID that eKashu used during the checkout process which is the order number # $sActinicFormatOriginalData .= '&x_trans_id=' . $::g_PaymentInfo{'ORDERNUMBER'}; # # The currency # $sActinicFormatOriginalData .= '&CU=' . $pmapInputNameToValue->{'ekashu_currency'}; # # Add the eKashu generated hash as the signature for the payment # this will be checked by the Actinic windows application when orders # and PSP information are downloaded into the application. my $sParams = $sActinicFormatOriginalData; $sParams =~ s/.+&(ON=.+)/$1/; my $sSignature = ACTINIC::GetMD5Hash($sParams); # md5 of the params between ON and SN as the blob signature # $sActinicFormatOriginalData .= '&SN=' . $sSignature; # # Verification results # $sActinicFormatOriginalData .= '&RA=' . $pmapInputNameToValue->{'ekashu_card_address_result'}; $sActinicFormatOriginalData .= '&RC=' . $pmapInputNameToValue->{'ekashu_verification_value_result'}; $sActinicFormatOriginalData .= '&PR=' . $pmapInputNameToValue->{'ekashu_auth_result'}; $sActinicFormatOriginalData .= '&ZR=' . $pmapInputNameToValue->{'ekashu_card_zip_code_result'}; $sActinicFormatOriginalData .= '&3R=' . $pmapInputNameToValue->{'ekashu_3d_secure_v2_result'}; $sActinicFormatOriginalData .= '&3E=' . $pmapInputNameToValue->{'ekashu_3d_secure_v2_enrolled'}; $sActinicFormatOriginalData .= '&CH=' . $pmapInputNameToValue->{'ekashu_card_hash'}; $sActinicFormatOriginalData .= '&CR=' . $pmapInputNameToValue->{'ekashu_card_reference'}; $sActinicFormatOriginalData .= '&CS=' . $pmapInputNameToValue->{'ekashu_card_scheme'}; $sActinicFormatOriginalData .= '&CE=' . $pmapInputNameToValue->{'ekashu_expires_end_month'} . '/' . $pmapInputNameToValue->{'ekashu_expires_end_year'}; $sActinicFormatOriginalData .= '&CM=' . $pmapInputNameToValue->{'ekashu_masked_card_number'}; # # This block of code isolates the plug-in scripts from version specific code that they have historically used # BEGIN undef $sError; if (defined $::EC_MAJOR_VERSION) # EC version 6 or greater { $sError = RecordAuthorization(\$sActinicFormatOriginalData); } else # Pre-version 6 { # # Fool RecordAuthorization by ditching the original input string # $::g_OriginalInputData = $sActinicFormatOriginalData; $sError = RecordAuthorization(); } # END # This block of code isolates the plug-in scripts from version specific code that they have historically used if (length $sError != 0) # if there were any errors, { ACTINIC::PrintPSPResponse('text/plain', $sError, '500 Internal Server Error'); return ($::FAILURE, $sError); } else { ACTINIC::PrintPSPResponse('text/plain', "OK"); } return ($::SUCCESS, ""); } ####################################################### # # ReadPostData - read the posted data. It is still in # the queue because the Actinic code only expects # GET or POST data, but not both and it handles GET # first. # # Expects: $::ENV{CONTENT_LENGTH} to be defined # STDIN to contain the POST data # # Returns: 0 - status # 1 - error if any # 2 - reference to hash containing parameters # 3 - the raw posted data string # ####################################################### sub ReadPostData { my ($InputData, $nInputLength, $nStep, $InputBuffer); $nInputLength = 0; $nStep = 0; while ($nInputLength != $::ENV{'CONTENT_LENGTH'}) # read until you have the entire chunk of data { # # read the input # binmode STDIN; $nStep = read(STDIN, $InputBuffer, $ENV{'CONTENT_LENGTH'}); # read POSTed data $nInputLength += $nStep; # keep track of the total data length $InputData .= $InputBuffer; # append the latest chunk to the total data buffer if (0 == $nStep) # EOF { last; # stop read } } if ($nInputLength != $ENV{'CONTENT_LENGTH'}) { return ($::FAILURE, "Bad input. The data length actually read ($nInputLength) does not match the length specified " . $ENV{'CONTENT_LENGTH'} . "\n", undef, undef); } $InputData =~ s/&$//; # loose any bogus trailing &'s # # Parse the input # my (@listNameValues); @listNameValues = split (/[&=]/, $InputData); # break the input into key/values if ($#listNameValues % 2 != 1) # if there is an unmatched value, it is a trailing = which means the value is undef { push @listNameValues, undef; } # # Decode the input # my %EncodedInput = @listNameValues; # map the input key/values to a hash = note that this doesn't work for things like mult-select lists but we don't have to worry about that here my ($key, $value); my %mapNameToValue; while (($key, $value) = each %EncodedInput) { $mapNameToValue{DecodeText($key)} = DecodeText($value); } return ($::SUCCESS, undef, \%mapNameToValue, $InputData); } ####################################################### # # DecodeText - decode the CGI FORM encoding # # Inputs: 0 - string to decode # # Returns: decoded string # ####################################################### sub DecodeText { my ($sString) = @_; $sString =~ s/\+/ /g; # replace + signs with the spaces they represent $sString =~ s/%([A-Fa-f0-9]{2})/pack('c',hex($1))/ge; # Convert %XX from hex numbers to character equivalent return $sString; } ####################################################### # # DisplayOCCSuccessResponsePage - display the OCC Response # page # # Returns: 0 - status # 1 - error if any # 2 - html # ####################################################### sub DisplayOCCSuccessResponsePage { my %mapVarTable; # # plug the values into a substitution hash # $mapVarTable{$::VARPREFIX.'CALLBACKURL'} = $::g_InputHash{'CALLBACKURLUSER'}; $mapVarTable{$::VARPREFIX.'HIDDENVALUES'} = $Response[3]; $mapVarTable{$::VARPREFIX.'ORDERNUMBER'} = $::g_InputHash{'ON'}; $mapVarTable{$::VARPREFIX.'CATALOGROOT'} = $::g_sWebSiteUrl; $mapVarTable{$::VARPREFIX.'STYLESHEETURL'} = $::g_sActPayStyleSheetURL; if(defined $::g_InputHash{TM}) { $mapVarTable{$::VARPREFIX.'TXNIDMESSAGE'} = "Test Transaction. No charge was made to your Credit Card."; } # # substitute the values into the template # my $sFileName = $::sPath . 'Act_OCCOK.html'; if(defined $::g_pPaymentList) { # v4.5 and above # $sFileName = $::sPath . 'Act_OCCActPayOK.html'; } # # make the substitutions # return (DisplayTemplateFile($sFileName, \%mapVarTable)); } ####################################################### # # DisplayOCCErrorResponsePage - display the OCC error # response page # # Returns: 0 - status # 1 - error if any # 2 - html # ####################################################### sub DisplayOCCErrorResponsePage { my %mapVarTable; # # plug the values into a substitution hash # $mapVarTable{$::VARPREFIX.'CALLBACKURL'} = $::g_InputHash{'CALLBACKURL'}; $mapVarTable{$::VARPREFIX.'HIDDENVALUES'} = $Response[3]; $mapVarTable{$::VARPREFIX.'CATALOGROOT'} = $::g_sWebSiteUrl; $mapVarTable{$::VARPREFIX.'STYLESHEETURL'} = $::g_sActPayStyleSheetURL; # # substitute the values into the template # my $sFileName = $::sPath . 'Act_OCCError.html'; if(defined $::g_pPaymentList) { # v4.5 and above # $sFileName = $::sPath . 'Act_OCCActPayError.html'; } # # make the substitutions # return (DisplayTemplateFile($sFileName, \%mapVarTable)); } ####################################################### # # DisplayTemplateFile - make substitutions in the given template file # # Input: 0 - path to data directory # 1 - variable map # # Returns: 0 - status # 1 - error if any # 2 - html # ####################################################### sub DisplayTemplateFile { my ($sFileName, $pVarTable) = @_; # # make the substitutions # @Response = ACTINIC::TemplateFile($sFileName, $pVarTable); if ($Response[0] != $::SUCCESS) { return (@Response); } my $sHTML = $Response[2]; # # process the test mode warning # my ($sOnlineDelimiter) = $::DELPREFIX . 'ONLINE'; my ($sOfflineDelimiter) = $::DELPREFIX . 'OFFLINE'; # # only include the test mode block if we are in test mode # if ($::g_InputHash{'ACTION'} !~ m/^OFFLINE_AUTHORIZE_/i) { # # remove the delimiter text # $sHTML =~ s/$sOfflineDelimiter//g; # # remove the test mode warning blob (/s removes the \n limitation of .) # $sHTML =~ s/$sOnlineDelimiter(.*?)$sOnlineDelimiter//gs; } else { # # remove the test mode warning blob (/s removes the \n limitation of .) # $sHTML =~ s/$sOfflineDelimiter(.*?)$sOfflineDelimiter//gs; # # remove the delimiter text # $sHTML =~ s/$sOnlineDelimiter//g; } return ($::SUCCESS, '', $sHTML); }