From cfaee245c03cfc3698503a794224b93c96e48b7b Mon Sep 17 00:00:00 2001 From: eileen <emcnaughton@wikimedia.org> Date: Wed, 10 Jun 2020 13:32:15 +1200 Subject: [PATCH] dev/financial#131 Fix Elavon processor to throw exceptions In line with our goal of modelling 'good behaviour' in core processors --- CRM/Core/Payment/Elavon.php | 115 +++++++++++------------------------- 1 file changed, 35 insertions(+), 80 deletions(-) diff --git a/CRM/Core/Payment/Elavon.php b/CRM/Core/Payment/Elavon.php index 35a93702e1c..8990e858603 100644 --- a/CRM/Core/Payment/Elavon.php +++ b/CRM/Core/Payment/Elavon.php @@ -9,6 +9,8 @@ +----------------------------------------------------------------------------+ */ +use Civi\Payment\Exception\PaymentProcessorException; + /** * ----------------------------------------------------------------------------------------------- * The basic functionality of this processor is that variables from the $params object are transformed @@ -24,9 +26,6 @@ * ----------------------------------------------------------------------------------------------- */ class CRM_Core_Payment_Elavon extends CRM_Core_Payment { - // (not used, implicit in the API, might need to convert?) - const - CHARSET = 'UFT-8'; /** * Constructor. @@ -34,9 +33,7 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment { * @param string $mode * The mode of operation: live or test. * - * @param $paymentProcessor - * - * @return CRM_Core_Payment_Elavon + * @param array $paymentProcessor */ public function __construct($mode, &$paymentProcessor) { // live or test @@ -45,22 +42,17 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment { } /** + * Map fields to parameters. + * * This function is set up and put here to make the mapping of fields * from the params object as visually clear as possible for easy editing * - * Comment out irrelevant fields - * @param $params + * @param array $params + * * @return array */ public function mapProcessorFieldstoParams($params) { - - // compile array - // Payment Processor field name fields from $params array - // credit card name $requestFields['ssl_first_name'] = $params['billing_first_name']; - // credit card name - //$requestFields['ssl_middle_name'] = $params['billing_middle_name']; - // credit card name $requestFields['ssl_last_name'] = $params['billing_last_name']; // contact name $requestFields['ssl_ship_to_first_name'] = $params['first_name']; @@ -86,28 +78,17 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment { // Added two lines below to allow commercial cards to go through as per page 15 of Elavon developer guide $requestFields['ssl_customer_code'] = '1111'; $requestFields['ssl_salestax'] = 0.0; - - /************************************************************************************ - * Fields available from civiCRM not implemented for Elavon - * - * $params['qfKey']; - * $params['amount_other']; - * $params['ip_address']; - * $params['contributionType_name' ]; - * $params['contributionPageID']; - * $params['contributionType_accounting_code']; - * $params['amount_level']; - * $params['credit_card_type']; - ************************************************************************************/ return $requestFields; } /** - * This function sends request and receives response from - * the processor + * This function sends request and receives response from the processor. + * * @param array $params - * @return array|object - * @throws Exception + * + * @return array + * + * @throws \CRM_Core_Exception */ public function doDirectPayment(&$params) { if (isset($params['is_recur']) && $params['is_recur'] == TRUE) { @@ -120,7 +101,7 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment { //Create the array of variables to be sent to the processor from the $params array // passed into this function - $requestFields = self::mapProcessorFieldstoParams($params); + $requestFields = $this->mapProcessorFieldstoParams($params); // define variables for connecting with the gateway $requestFields['ssl_merchant_id'] = $this->_paymentProcessor['user_name']; @@ -128,7 +109,7 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment { $requestFields['ssl_pin'] = $this->_paymentProcessor['signature'] ?? NULL; $host = $this->_paymentProcessor['url_site']; - if ($this->_mode == "test") { + if ($this->_mode === 'test') { $requestFields['ssl_test_mode'] = "TRUE"; } @@ -137,11 +118,11 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment { // Check to see if we have a duplicate before we send if ($this->checkDupe($params['invoiceID'], CRM_Utils_Array::value('contributionID', $params))) { - return self::errorExit(9003, 'It appears that this transaction is a duplicate. Have you already submitted the form once? If so there may have been a connection problem. Check your email for a receipt. If you do not receive a receipt within 2 hours you can try your transaction again. If you continue to have problems please contact the site administrator.'); + throw new PaymentProcessorException('It appears that this transaction is a duplicate. Have you already submitted the form once? If so there may have been a connection problem. Check your email for a receipt. If you do not receive a receipt within 2 hours you can try your transaction again. If you continue to have problems please contact the site administrator.', 9003); } // Convert to XML using function below - $xml = self::buildXML($requestFields); + $xml = $this->buildXML($requestFields); // Send to the payment processor using cURL @@ -149,7 +130,7 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment { $ch = curl_init($chHost); if (!$ch) { - return self::errorExit(9004, 'Could not initiate connection to payment gateway'); + throw new PaymentProcessorException('Could not initiate connection to payment gateway', 9004); } curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, Civi::settings()->get('verifySSL') ? 2 : 0); @@ -182,13 +163,9 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment { // Paranoia - in the unlikley event that 'curl' error fails if (strlen($errorDesc) == 0) { - $errorDesc = "Connection to payment gateway failed"; + $errorDesc = 'Connection to payment gateway failed'; } - if ($errorNum = 60) { - return self::errorExit($errorNum, "Curl error - " . $errorDesc . " Try this link for more information http://curl.haxx.se/docs/sslcerts.html"); - } - - return self::errorExit($errorNum, "Curl error - " . $errorDesc . " your key is located at " . $key . " the url is " . $host . " xml is " . $requestxml . " processor response = " . $processorResponse); + throw new PaymentProcessorException('Curl error - ' . $errorDesc . ' Try this link for more information http://curl.haxx.se/docs/sslcerts.html', $errorNum); } // If null data returned - tell 'em and bail out @@ -196,13 +173,13 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment { // any reason, the return value will be the boolean false. if (($responseData === FALSE) || (strlen($responseData) == 0)) { curl_close($ch); - return self::errorExit(9006, "Error: Connection to payment gateway failed - no data returned."); + throw new PaymentProcessorException('Error: Connection to payment gateway failed - no data returned.', 9006); } // If gateway returned no data - tell 'em and bail out if (empty($responseData)) { curl_close($ch); - return self::errorExit(9007, "Error: No data returned from payment gateway."); + throw new PaymentProcessorException('Error: No data returned from payment gateway.', 9007); } // Success so far - close the curl and check the data @@ -210,36 +187,33 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment { // Payment successfully sent to gateway - process the response now - $processorResponse = self::decodeXMLResponse($responseData); + $processorResponse = $this->decodeXMLresponse($responseData); // success in test mode returns response "APPROVED" // test mode always returns trxn_id = 0 // fix for CRM-2566 if ($processorResponse['errorCode']) { - return self::errorExit(9010, "Error: [" . $processorResponse['errorCode'] . " " . $processorResponse['errorName'] . " " . $processorResponse['errorMessage'] . "] - from payment processor"); + throw new PaymentProcessorException("Error: [" . $processorResponse['errorCode'] . " " . $processorResponse['errorName'] . " " . $processorResponse['errorMessage'] . '] - from payment processor', 9010); } - if ($processorResponse['ssl_result_message'] == "APPROVED") { - if ($this->_mode == 'test') { + if ($processorResponse['ssl_result_message'] === "APPROVED") { + if ($this->_mode === 'test') { $query = "SELECT MAX(trxn_id) FROM civicrm_contribution WHERE trxn_id LIKE 'test%'"; - $p = []; - $trxn_id = strval(CRM_Core_DAO::singleValueQuery($query, $p)); - $trxn_id = str_replace('test', '', $trxn_id); - $trxn_id = intval($trxn_id) + 1; + $trxn_id = (string) CRM_Core_DAO::singleValueQuery($query); + $trxn_id = (int) str_replace('test', '', $trxn_id); + ++$trxn_id; $params['trxn_id'] = sprintf('test%08d', $trxn_id); return $params; } - else { - return self::errorExit(9099, "Error: [approval code related to test transaction but mode was " . $this->_mode); - } + throw new PaymentProcessorException('Error: [approval code related to test transaction but mode was ' . $this->_mode, 9099); } // transaction failed, print the reason - if ($processorResponse['ssl_result_message'] != "APPROVAL") { - return self::errorExit(9009, "Error: [" . $processorResponse['ssl_result_message'] . " " . $processorResponse['ssl_result'] . "] - from payment processor"); + if ($processorResponse['ssl_result_message'] !== "APPROVAL") { + throw new PaymentProcessorException('Error: [' . $processorResponse['ssl_result_message'] . ' ' . $processorResponse['ssl_result'] . '] - from payment processor', 9009); } else { // Success ! - if ($this->_mode != 'test') { + if ($this->_mode !== 'test') { // 'trxn_id' is varchar(255) field. returned value is length 37 $params['trxn_id'] = $processorResponse['ssl_txn_id']; } @@ -250,23 +224,6 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment { } } - /** - * Produces error message and returns from class. - * @param string $errorCode - * @param string $errorMessage - * @return CRM_Core_Error - */ - public function &errorExit($errorCode = NULL, $errorMessage = NULL) { - $e = CRM_Core_Error::singleton(); - if ($errorCode) { - $e->push($errorCode, 0, NULL, $errorMessage); - } - else { - $e->push(9000, 0, NULL, 'Unknown System Error.'); - } - return $e; - } - /** * This public function checks to see if we have the right processor config values set. * @@ -291,9 +248,7 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment { if (!empty($errorMsg)) { return implode('<p>', $errorMsg); } - else { - return NULL; - } + return NULL; } /** @@ -383,7 +338,7 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment { if (($pos1 === FALSE) || ($pos2 === FALSE)) { - return ""; + return ''; } -- GitLab