Skip to content
Snippets Groups Projects
Commit cfaee245 authored by eileen's avatar eileen
Browse files

financial#131 Fix Elavon processor to throw exceptions

In line with our goal of modelling 'good behaviour' in core processors
parent 7c907de0
No related branches found
No related tags found
No related merge requests found
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
+----------------------------------------------------------------------------+ +----------------------------------------------------------------------------+
*/ */
use Civi\Payment\Exception\PaymentProcessorException;
/** /**
* ----------------------------------------------------------------------------------------------- * -----------------------------------------------------------------------------------------------
* The basic functionality of this processor is that variables from the $params object are transformed * The basic functionality of this processor is that variables from the $params object are transformed
...@@ -24,9 +26,6 @@ ...@@ -24,9 +26,6 @@
* ----------------------------------------------------------------------------------------------- * -----------------------------------------------------------------------------------------------
*/ */
class CRM_Core_Payment_Elavon extends CRM_Core_Payment { class CRM_Core_Payment_Elavon extends CRM_Core_Payment {
// (not used, implicit in the API, might need to convert?)
const
CHARSET = 'UFT-8';
/** /**
* Constructor. * Constructor.
...@@ -34,9 +33,7 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment { ...@@ -34,9 +33,7 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment {
* @param string $mode * @param string $mode
* The mode of operation: live or test. * The mode of operation: live or test.
* *
* @param $paymentProcessor * @param array $paymentProcessor
*
* @return CRM_Core_Payment_Elavon
*/ */
public function __construct($mode, &$paymentProcessor) { public function __construct($mode, &$paymentProcessor) {
// live or test // live or test
...@@ -45,22 +42,17 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment { ...@@ -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 * 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 * from the params object as visually clear as possible for easy editing
* *
* Comment out irrelevant fields * @param array $params
* @param $params *
* @return array * @return array
*/ */
public function mapProcessorFieldstoParams($params) { 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']; $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']; $requestFields['ssl_last_name'] = $params['billing_last_name'];
// contact name // contact name
$requestFields['ssl_ship_to_first_name'] = $params['first_name']; $requestFields['ssl_ship_to_first_name'] = $params['first_name'];
...@@ -86,28 +78,17 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment { ...@@ -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 // 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_customer_code'] = '1111';
$requestFields['ssl_salestax'] = 0.0; $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; return $requestFields;
} }
/** /**
* This function sends request and receives response from * This function sends request and receives response from the processor.
* the processor *
* @param array $params * @param array $params
* @return array|object *
* @throws Exception * @return array
*
* @throws \CRM_Core_Exception
*/ */
public function doDirectPayment(&$params) { public function doDirectPayment(&$params) {
if (isset($params['is_recur']) && $params['is_recur'] == TRUE) { if (isset($params['is_recur']) && $params['is_recur'] == TRUE) {
...@@ -120,7 +101,7 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment { ...@@ -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 //Create the array of variables to be sent to the processor from the $params array
// passed into this function // passed into this function
$requestFields = self::mapProcessorFieldstoParams($params); $requestFields = $this->mapProcessorFieldstoParams($params);
// define variables for connecting with the gateway // define variables for connecting with the gateway
$requestFields['ssl_merchant_id'] = $this->_paymentProcessor['user_name']; $requestFields['ssl_merchant_id'] = $this->_paymentProcessor['user_name'];
...@@ -128,7 +109,7 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment { ...@@ -128,7 +109,7 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment {
$requestFields['ssl_pin'] = $this->_paymentProcessor['signature'] ?? NULL; $requestFields['ssl_pin'] = $this->_paymentProcessor['signature'] ?? NULL;
$host = $this->_paymentProcessor['url_site']; $host = $this->_paymentProcessor['url_site'];
if ($this->_mode == "test") { if ($this->_mode === 'test') {
$requestFields['ssl_test_mode'] = "TRUE"; $requestFields['ssl_test_mode'] = "TRUE";
} }
...@@ -137,11 +118,11 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment { ...@@ -137,11 +118,11 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment {
// Check to see if we have a duplicate before we send // Check to see if we have a duplicate before we send
if ($this->checkDupe($params['invoiceID'], CRM_Utils_Array::value('contributionID', $params))) { 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 // Convert to XML using function below
$xml = self::buildXML($requestFields); $xml = $this->buildXML($requestFields);
// Send to the payment processor using cURL // Send to the payment processor using cURL
...@@ -149,7 +130,7 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment { ...@@ -149,7 +130,7 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment {
$ch = curl_init($chHost); $ch = curl_init($chHost);
if (!$ch) { 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); 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 { ...@@ -182,13 +163,9 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment {
// Paranoia - in the unlikley event that 'curl' error fails // Paranoia - in the unlikley event that 'curl' error fails
if (strlen($errorDesc) == 0) { if (strlen($errorDesc) == 0) {
$errorDesc = "Connection to payment gateway failed"; $errorDesc = 'Connection to payment gateway failed';
} }
if ($errorNum = 60) { throw new PaymentProcessorException('Curl error - ' . $errorDesc . ' Try this link for more information http://curl.haxx.se/docs/sslcerts.html', $errorNum);
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);
} }
// If null data returned - tell 'em and bail out // If null data returned - tell 'em and bail out
...@@ -196,13 +173,13 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment { ...@@ -196,13 +173,13 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment {
// any reason, the return value will be the boolean false. // any reason, the return value will be the boolean false.
if (($responseData === FALSE) || (strlen($responseData) == 0)) { if (($responseData === FALSE) || (strlen($responseData) == 0)) {
curl_close($ch); 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 gateway returned no data - tell 'em and bail out
if (empty($responseData)) { if (empty($responseData)) {
curl_close($ch); 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 // Success so far - close the curl and check the data
...@@ -210,36 +187,33 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment { ...@@ -210,36 +187,33 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment {
// Payment successfully sent to gateway - process the response now // Payment successfully sent to gateway - process the response now
$processorResponse = self::decodeXMLResponse($responseData); $processorResponse = $this->decodeXMLresponse($responseData);
// success in test mode returns response "APPROVED" // success in test mode returns response "APPROVED"
// test mode always returns trxn_id = 0 // test mode always returns trxn_id = 0
// fix for CRM-2566 // fix for CRM-2566
if ($processorResponse['errorCode']) { 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 ($processorResponse['ssl_result_message'] === "APPROVED") {
if ($this->_mode == 'test') { if ($this->_mode === 'test') {
$query = "SELECT MAX(trxn_id) FROM civicrm_contribution WHERE trxn_id LIKE 'test%'"; $query = "SELECT MAX(trxn_id) FROM civicrm_contribution WHERE trxn_id LIKE 'test%'";
$p = []; $trxn_id = (string) CRM_Core_DAO::singleValueQuery($query);
$trxn_id = strval(CRM_Core_DAO::singleValueQuery($query, $p)); $trxn_id = (int) str_replace('test', '', $trxn_id);
$trxn_id = str_replace('test', '', $trxn_id); ++$trxn_id;
$trxn_id = intval($trxn_id) + 1;
$params['trxn_id'] = sprintf('test%08d', $trxn_id); $params['trxn_id'] = sprintf('test%08d', $trxn_id);
return $params; return $params;
} }
else { throw new PaymentProcessorException('Error: [approval code related to test transaction but mode was ' . $this->_mode, 9099);
return self::errorExit(9099, "Error: [approval code related to test transaction but mode was " . $this->_mode);
}
} }
// transaction failed, print the reason // transaction failed, print the reason
if ($processorResponse['ssl_result_message'] != "APPROVAL") { if ($processorResponse['ssl_result_message'] !== "APPROVAL") {
return self::errorExit(9009, "Error: [" . $processorResponse['ssl_result_message'] . " " . $processorResponse['ssl_result'] . "] - from payment processor"); throw new PaymentProcessorException('Error: [' . $processorResponse['ssl_result_message'] . ' ' . $processorResponse['ssl_result'] . '] - from payment processor', 9009);
} }
else { else {
// Success ! // Success !
if ($this->_mode != 'test') { if ($this->_mode !== 'test') {
// 'trxn_id' is varchar(255) field. returned value is length 37 // 'trxn_id' is varchar(255) field. returned value is length 37
$params['trxn_id'] = $processorResponse['ssl_txn_id']; $params['trxn_id'] = $processorResponse['ssl_txn_id'];
} }
...@@ -250,23 +224,6 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment { ...@@ -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. * 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 { ...@@ -291,9 +248,7 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment {
if (!empty($errorMsg)) { if (!empty($errorMsg)) {
return implode('<p>', $errorMsg); return implode('<p>', $errorMsg);
} }
else { return NULL;
return NULL;
}
} }
/** /**
...@@ -383,7 +338,7 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment { ...@@ -383,7 +338,7 @@ class CRM_Core_Payment_Elavon extends CRM_Core_Payment {
if (($pos1 === FALSE) || ($pos2 === FALSE)) { if (($pos1 === FALSE) || ($pos2 === FALSE)) {
return ""; return '';
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment