# Converts Export Script callback from InternetSecure # into an combined OCC_VALIDATE/AUTHORIZE callback that looks like the # return call from Authorize.Net # # Called via CallPlugInScript() when ACT_POSTPROCESS parameter is set # # Will exit with message and error log if any problems # Will create ORDERNUMBER.occ authorization on success # # Inherits %::g_InputHash, %ENV, and other globals from OrderScript.pl # my @Response; # return values from function calls my %ValidateValues; # results of OCC_VALIDATE processing # # Needed by AbortProcessing so must be defined early # my $sPath = $::g_InputHash{PATH}; # # record important data in error.err # 0 - none # 1 - results of processing # 2 - log input data too # my $DEBUGLOG = 0; if ($DEBUGLOG >= 2) { ACTINIC::RecordErrors("INITIAL: $::g_OriginalInputData", $sPath); } # # The 'native' currencies accepted by InternetSecure # my %Currencies = ( '0' => 'CAD', '1' => 'USD', ); # # The callback will ALWAYS come from here, so confirm it # unless ($ENV{'REMOTE_HOST'} eq 'www.internetsecure.com') { my $sError = <<"ERROREND"; Invalid Callback: REMOTE_HOST = $ENV{'REMOTE_HOST'} REMOTE_ADDR = $ENV{'REMOTE_ADDR'} ERROREND # AbortProcessing($sError, $sPath); } unless ($::g_InputHash{'ApprovalCode'}) { $::g_InputHash{'ApprovalCode'} = '000000'; } # # Check for InternetSecure set to Test Mode # if (($::g_InputHash{'ApprovalCode'} eq '000000') && ($::g_InputHash{'SalesOrderNumber'} =~ /^[0]+$/)) { $::g_InputHash{TM} = '1'; } # # OCC_VALIDATE emulation # if(defined $::g_InputHash{CARTID}) { # "local" restricts scope of the globals to this routine # local ($::g_sCartId, %::g_BillContact, %::g_ShipContact, %::g_ShipInfo, %::g_TaxInfo, %::g_GeneralInfo, %::g_PaymentInfo, %::g_LocationInfo); # # read the cart ID from the input hash as if "ACTION=OCC_VALIDATE" # $::g_sCartId = $::g_InputHash{CARTID}; # # read the checkout status for this Cart # my ($Status, $Message, $pBillContact, $pShipContact, $pShipInfo, $pTaxInfo, $pGeneralInfo, $pPaymentInfo, $pLocationInfo) = ActinicOrder::RetrieveCheckoutStatus($sPath, $::g_sCartId); if ($Status != $::SUCCESS) { AbortProcessing("Cart = $::g_sCartId, Error = $Message\n", $sPath); } # # copy the hashes to global tables # %::g_BillContact = %$pBillContact; %::g_ShipContact = %$pShipContact; %::g_ShipInfo = %$pShipInfo; %::g_TaxInfo = %$pTaxInfo; %::g_GeneralInfo = %$pGeneralInfo; %::g_PaymentInfo = %$pPaymentInfo; %::g_LocationInfo = %$pLocationInfo; # # Should have same environment as OCC_VALIDATE callback # @Response = GetOCCValidationData(); unless ($Response[0] == $::SUCCESS) { AbortProcessing("OCC_VALIDATE: $Response[1]\n", $sPath); } # if ($DEBUGLOG >= 2) { ACTINIC::RecordErrors("VALIDATE: $Response[2]\n", $sPath); } # # split the data into NAME/VALUE pairs # my (@arrNameValuePairs) = split /&/, $Response[2]; my ($sName, $sValue, $sNameValuePair); foreach $sNameValuePair (@arrNameValuePairs) { ($sName, $sValue) = split /=/, $sNameValuePair; # # add the name to the ValidateValues hash # $ValidateValues{$sName} = $sValue; } } else { AbortProcessing("CARTID not specified\n", $sPath); } # # 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 # unless ($ValidateValues{ORDERNUMBER} eq $::g_InputHash{ON}) { AbortProcessing("ORDERNUMBER $::g_InputHash{ON} does not match $ValidateValues{ORDERNUMBER}\n", $sPath); } $sActinicFormatOriginalData .= '&ON=' . $::g_InputHash{ON}; # # Check the supported Currencies # unless ($ValidateValues{CURRENCY} eq $Currencies{$::g_InputHash{Currency}}) { my $sError = "CURRENCY $Currencies{$::g_InputHash{Currency}} ($::g_InputHash{Currency}) does not match $ValidateValues{CURRENCY}\n"; AbortProcessing($sError, $sPath); } # # Add the amount # my $ActAmount = $::g_InputHash{Amount} * $ValidateValues{FACTOR}; # # must use floating point comparison to cover rounding errors in conversion # from the Perl Cookbook recipe 2.2 # unless (sprintf("%.1g", $ValidateValues{AMOUNT}) eq sprintf("%.1g", $ActAmount)) { AbortProcessing("AMOUNT $ActAmount ($::g_InputHash{Amount}) does not match $ValidateValues{AMOUNT}\n", $sPath); } $sActinicFormatOriginalData .= '&AM=' . "$ValidateValues{AMOUNT}"; $sActinicFormatOriginalData .= '&Amount=' . $::g_InputHash{Amount}; # # Add the authorisation_code:transaction_id # my $sTransID = $::g_InputHash{receiptnumber}; $sActinicFormatOriginalData .= '&CD=' . $::g_InputHash{ApprovalCode}; $sActinicFormatOriginalData .= ':' . $sTransID; # # Add the InternetSecure transaction ID # Signature also requires AuthorizeNet name # $sActinicFormatOriginalData .= '&TX=' . $sTransID; $sActinicFormatOriginalData .= '&x_trans_id=' . $sTransID; # # Get the current date/time on the server # my ($sDate) = ACTINIC::GetActinicDate(); ($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}; } # # MerchantID # my $sMerchantID = $::g_InputHash{MerchantNumber}; $sActinicFormatOriginalData .= '&MerchantNumber=' . $sMerchantID; # # Use a signature from InternetSecure if provided # Otherwise synthesize one for download # if(defined $::g_InputHash{xxxSignature}) { $sActinicFormatOriginalData .= '&SN=' . $::g_InputHash{xxxSignature}; } else { # Signature Initialization: load the available version of the MD5 module # my $rSignMD5; eval { require Digest::MD5; # Try loading the compiled module $rSignMD5 = Digest::MD5->new(); }; if ($@) { eval # Use Perl version if not found { require Digest::Perl::MD5; }; if ($@) # Use the untranslated version { # as a last resort require DigestPerlMD5; } $rSignMD5 = Digest::Perl::MD5->new(); } # # Add the InternetSecure signature using the Authorize.Net formula # my $sTransAmount = sprintf ("%.2f", $::g_InputHash{Amount}); # # Signature requires AuthorizeNet name # $sActinicFormatOriginalData .= '&x_amount=' . $sTransAmount; # $rSignMD5->reset(); $rSignMD5->add("$sMerchantID$sMerchantID$sTransID$sTransAmount"); # $sActinicFormatOriginalData .= '&SN=' . uc($rSignMD5->hexdigest()); } # # Fool RecordAuthorization by ditching the original input string # $::g_OriginalInputData = $sActinicFormatOriginalData; # log the results to error.err when debugging if ($DEBUGLOG) { ACTINIC::RecordErrors("FINAL: $::g_OriginalInputData", $sPath); } my $sError = RecordAuthorization(); if (length $sError != 0) # if there were any errors, { # record any error to error.err # AbortProcessing($sError, $sPath); } else { ACTINIC::PrintText('Transaction recorded successfully'); } # # processing is complete at this point # return ($::SUCCESS); ####################################################### # # Exit returning error message to InternetSecure and also logging # it in error.err # Params: $sError - Message # $sPath - Path for RecordErrors # # Returns: nothing (does not return) # ####################################################### sub AbortProcessing { my ($sError, $sPath) = @_; ACTINIC::RecordErrors($sError, $sPath); ACTINIC::PrintText($sError); exit; }