635349; # # This block of code isolates the plug-in scripts from version specific code that they have historically used # BEGIN my $sPath; if (defined $::EC_MAJOR_VERSION) # EC version 6 or greater { $sPath = $::sPath; } else # Pre-version 6 { $sPath = $::g_InputHash{PATH}; } # END # # This block of code isolates the plug-in scripts from version specific code that they have historically used # # # PayPal post's it's data so let's read it here if necessary # my ($status, $sError, $pmapInputNameToValue, $sPostedData) = ReadPostData(); if ($status != $::SUCCESS) { ACTINIC::RecordErrors($sError, $sPath); # record the error to error.err return ($::FAILURE); } if (0 == scalar keys %$pmapInputNameToValue) # if the POST data is empty, it was already read { ACTINIC::RecordErrors("No POSTED data recieved from PayPal callback.", $sPath); # record the error to error.err return ($::FAILURE); } # # Pass along the callback for confirmation to paypal. # my ($sHttpStatus, $sResponse); $sPostedData .= '&cmd=_notify-validate'; if ((defined $::EC_MAJOR_VERSION) && ($::EC_MAJOR_VERSION >= 8)) # EC version 8 or greater, then we use https interface to PayPal server, so it can't be filtered. { ($status, $sError, $sHttpStatus, $sResponse) = ACTINIC::HTTPS_SendAndReceive('www.paypal.com', 443, '/cgi-bin/webscr', $sPostedData, 'POST'); } else { ($status, $sError, $sHttpStatus, $sResponse) = ACTINIC::HTTPS_SendAndReceive('www.paypal.com', 443, '/cgi-bin/webscr', $sPostedData, 'POST'); } if ($status != $::SUCCESS || # transmission or $sHttpStatus !~ /200/) # processing failed { ACTINIC::RecordErrors($sError, $sPath); # record the error to error.err return ($::FAILURE); } # # SendAndReceive has a bug so the total response comes in the sHTTPStatus string. Let's just use it all here # $sResponse = $sHttpStatus . $sResponse; # # If the response is bad, abort # if ($sResponse !~ /VERIFIED/) { ACTINIC::RecordErrors("Invalid PayPal verification response \"$sResponse\"", $sPath); # record the error to error.err return ($::FAILURE); } # # If this callback is not noting the payment status as "complete", we are done for now - only record "completes" # if ($pmapInputNameToValue->{payment_status} ne "Completed") { my ($sPPMessage); $sPPMessage = "PayPal payment status: " . $pmapInputNameToValue->{payment_status}; # # if there is pending reason, then print it too. # if (length $pmapInputNameToValue->{pending_reason}) { $sPPMessage .= ", Pending reason: " . $pmapInputNameToValue->{pending_reason}; } ACTINIC::RecordErrors( $sPPMessage , $sPath); # record the error to error.err ACTINIC::PrintText("OK"); 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=' . $pmapInputNameToValue->{invoice}; # # if the payment was not made in USD then the amountis in the mc_gross # my ($nActAmount); if (length $pmapInputNameToValue->{payment_gross}) { $nActAmount = $pmapInputNameToValue->{payment_gross}; } else { $nActAmount = $pmapInputNameToValue->{mc_gross}; } # # Add the amount # $sActinicFormatOriginalData .= '&AM=' . $nActAmount * 100; # bit of a hack $sActinicFormatOriginalData .= '&x_amount=' . $nActAmount; # # Add the transaction_id # $sActinicFormatOriginalData .= '&CD=' . $pmapInputNameToValue->{txn_id}; # # 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}; } # # Add the transaction ID # $sActinicFormatOriginalData .= '&TX=' . $pmapInputNameToValue->{txn_id}; $sActinicFormatOriginalData .= '&x_trans_id=' . $pmapInputNameToValue->{txn_id}; # # if the payment was not made in USD then the currency is in the mc_currency # my ($nActCurrency); if (length $pmapInputNameToValue->{currency_code}) { $nActCurrency = $pmapInputNameToValue->{currency_code}; } else { $nActCurrency = $pmapInputNameToValue->{mc_currency}; } # # The currency # $sActinicFormatOriginalData .= '&CU=' . $nActCurrency; # # Create a bogus signature line # $sActinicFormatOriginalData .= '&SN=000'; # # 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::RecordErrors($sError, $sPath); # record the error to error.err } ACTINIC::PrintText("OK"); ####################################################### # # 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 PayPal 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; } return ($::SUCCESS);