/** * Called from do_transaction() in order to be able to deal with transactions that had * recoverable errors but that do require the entire transaction to be repeated. * * This function has the following extension hooks: * * pre_process_<strtolower($transaction)> * Called before the transaction is processed; intended to call setValidationAction() * if the transaction should not be performed. Anti-fraud can be performed in this * hook by calling $this->runAntifraudFilters(). * * * post_process_<strtolower($transaction)> * * @param string $transaction Name of the transaction being performed * @param &string() $retryVars Reference to an array of variables that caused the * transaction to fail. * * @return PaymentTransactionResponse * @throws UnexpectedValueException */ private final function do_transaction_internal($transaction, &$retryVars = null) { $this->debugarray[] = __FUNCTION__ . " is doing a {$transaction}."; //reset, in case this isn't our first time. $this->transaction_response = new PaymentTransactionResponse(); $this->final_status = false; $this->setValidationAction('process', true); $errCode = null; /* --- Build the transaction string for cURL --- */ try { $this->setCurrentTransaction($transaction); $this->executeIfFunctionExists('pre_process_' . $transaction); if ($this->getValidationAction() != 'process') { $this->logger->info("Failed pre-process checks for transaction type {$transaction}."); $this->transaction_response->setCommunicationStatus(false); $this->transaction_response->setMessage($this->getErrorMapByCodeAndTranslate('internal-0000')); $this->transaction_response->setErrors(array('internal-0000' => array('debugInfo' => "Failed pre-process checks for transaction type {$transaction}.", 'message' => $this->getErrorMapByCodeAndTranslate('internal-0000'), 'logLevel' => LogLevel::INFO))); return $this->transaction_response; } if (!$this->isBatchProcessor()) { // TODO: Maybe move this to the pre_process functions? $this->dataObj->saveContributionTrackingData(); } $commType = $this->getCommunicationType(); if ($commType === 'redirect') { //in the event that we have a redirect transaction that never displays the form, //save this most recent one before we leave. $this->session_pushFormName($this->getData_Unstaged_Escaped('ffname')); $this->transaction_response->setCommunicationStatus(true); // Build the redirect URL. $redirectUrl = $this->getProcessorUrl(); $redirectParams = $this->buildRequestParams(); if ($redirectParams) { // Add GET parameters, if provided. $redirectUrl .= '?' . http_build_query($redirectParams); } $this->transaction_response->setRedirect($redirectUrl); return $this->transaction_response; } elseif ($commType === 'xml') { $this->profiler->getStopwatch("buildRequestXML", true); // begin profiling $curlme = $this->buildRequestXML(); // build the XML $this->profiler->saveCommunicationStats("buildRequestXML", $transaction); // save profiling data } elseif ($commType === 'namevalue') { $this->profiler->getStopwatch("buildRequestNameValueString", true); // begin profiling $curlme = $this->buildRequestNameValueString(); // build the name/value pairs $this->profiler->saveCommunicationStats("buildRequestNameValueString", $transaction); // save profiling data } else { throw new UnexpectedValueException("Communication type of '{$commType}' unknown"); } } catch (Exception $e) { $this->logger->critical('Malformed gateway definition. Cannot continue: Aborting.\\n' . $e->getMessage()); $this->transaction_response->setCommunicationStatus(false); $this->transaction_response->setMessage($this->getErrorMapByCodeAndTranslate('internal-0001')); $this->transaction_response->setErrors(array('internal-0001' => array('debugInfo' => 'Malformed gateway definition. Cannot continue: Aborting.\\n' . $e->getMessage(), 'message' => $this->getErrorMapByCodeAndTranslate('internal-0001'), 'logLevel' => LogLevel::CRITICAL))); return $this->transaction_response; } /* --- Do the cURL request --- */ $this->profiler->getStopwatch(__FUNCTION__, true); $txn_ok = $this->curl_transaction($curlme); if ($txn_ok === true) { // We have something to slice and dice. $this->logger->info("RETURNED FROM CURL:" . print_r($this->transaction_response->getRawResponse(), true)); // Decode the response according to $this->getResponseType $formatted = $this->getFormattedResponse($this->transaction_response->getRawResponse()); // Process the formatted response. This will then drive the result action try { $this->processResponse($formatted); } catch (ResponseProcessingException $ex) { $errCode = $ex->getErrorCode(); $retryVars = $ex->getRetryVars(); $this->transaction_response->addError($errCode, array('message' => $this->getErrorMapByCodeAndTranslate('internal-0001'), 'debugInfo' => $ex->getMessage(), 'logLevel' => LogLevel::ERROR)); } } elseif ($txn_ok === false) { // nothing to process, so we have to build it manually $logMessage = 'Transaction Communication failed' . print_r($this->transaction_response, true); $this->logger->error($logMessage); $this->transaction_response->setCommunicationStatus(false); $this->transaction_response->setMessage($this->getErrorMapByCodeAndTranslate('internal-0002')); $this->transaction_response->setErrors(array('internal-0002' => array('debugInfo' => $logMessage, 'message' => $this->getErrorMapByCodeAndTranslate('internal-0002'), 'logLevel' => LogLevel::ERROR))); } // Log out how much time it took for the cURL request $this->profiler->saveCommunicationStats(__FUNCTION__, $transaction); if (!empty($retryVars)) { $this->logger->critical("{$transaction} Communication failed (errcode {$errCode}), will reattempt!"); // Set this by key so that the result object still has all the cURL data $this->transaction_response->setCommunicationStatus(false); $this->transaction_response->setMessage($this->getErrorMapByCodeAndTranslate($errCode)); $this->transaction_response->setErrors(array($errCode => array('debugInfo' => "{$transaction} Communication failed (errcode {$errCode}), will reattempt!", 'message' => $this->getErrorMapByCodeAndTranslate($errCode), 'logLevel' => LogLevel::CRITICAL))); } //if we have set errors by this point, the transaction is not okay $errors = $this->getTransactionErrors(); if (!empty($errors)) { $txn_ok = false; } // If we have any special post-process instructions for this // transaction, do 'em. // NOTE: If you want your transaction to fire off the post-process // logic, you need to run $this->postProcessDonation in a function // called // 'post_process' . strtolower($transaction) // in the appropriate gateway object. if ($txn_ok && empty($retryVars)) { $this->executeIfFunctionExists('post_process_' . $transaction); if ($this->getValidationAction() != 'process') { $this->logger->info("Failed post-process checks for transaction type {$transaction}."); $this->transaction_response->setCommunicationStatus(false); $this->transaction_response->setMessage($this->getErrorMapByCodeAndTranslate('internal-0000')); $this->transaction_response->setErrors(array('internal-0000' => array('debugInfo' => "Failed post-process checks for transaction type {$transaction}.", 'message' => $this->getErrorMapByCodeAndTranslate('internal-0000'), 'logLevel' => LogLevel::INFO))); return $this->transaction_response; } } // log that the transaction is essentially complete $this->logger->info('Transaction complete.'); if (!$this->isBatchProcessor()) { $this->debugarray[] = 'numAttempt = ' . $this->session_getData('numAttempt'); } return $this->transaction_response; }