_mode = $mode; $this->_paymentProcessor = $paymentProcessor; $this->_processorName = ts('eWay'); } /** * Singleton function used to manage this object * * @param string $mode * The mode of operation: live or test. * * @param object $paymentProcessor * @param null $paymentForm * @param bool $force * * @return object * @static */ public static function &singleton($mode, &$paymentProcessor, &$paymentForm = NULL, $force = false) { if (!empty($paymentProcessor['id'])) { $cacheKey = $paymentProcessor['id']; } else { //@todo eliminated instances of this in favour of id-specific instances. $cacheKey = $mode . '_' . $paymentProcessor['name']; } if (self::$_singleton[$cacheKey] === NULL) { self::$_singleton[$cacheKey] = new CRM_Core_Payment_eWAY($mode, $paymentProcessor); } return self::$_singleton[$cacheKey]; } /********************************************************** * This function sends request and receives response from * eWAY payment process **********************************************************/ public function doDirectPayment(&$params) { if (CRM_Utils_Array::value('is_recur', $params) == TRUE) { CRM_Core_Error::fatal(ts('eWAY - recurring payments not implemented')); } if (!defined('CURLOPT_SSLCERT')) { CRM_Core_Error::fatal(ts('eWAY - Gateway requires curl with SSL support')); } // eWAY Client ID $ewayCustomerID = $this->_paymentProcessor['user_name']; // eWAY Gateway URL $gateway_URL = $this->_paymentProcessor['url_site']; //------------------------------------ // create eWAY gateway objects //------------------------------------ $eWAYRequest = new GatewayRequest; if (($eWAYRequest == NULL) || (!($eWAYRequest instanceof GatewayRequest))) { return self::errorExit(9001, "Error: Unable to create eWAY Request object."); } $eWAYResponse = new GatewayResponse; if (($eWAYResponse == NULL) || (!($eWAYResponse instanceof GatewayResponse))) { return self::errorExit(9002, "Error: Unable to create eWAY Response object."); } /* //------------------------------------------------------------- // NOTE: eWAY Doesn't use the following at the moment: //------------------------------------------------------------- $creditCardType = $params['credit_card_type']; $currentcyID = $params['currencyID']; $country = $params['country']; */ //------------------------------------------------------------- // Prepare some composite data from _paymentProcessor fields //------------------------------------------------------------- $fullAddress = $params['street_address'] . ", " . $params['city'] . ", " . $params['state_province'] . "."; $expireYear = substr($params['year'], 2, 2); $expireMonth = sprintf('%02d', (int) $params['month']); // CiviCRM V1.9 - Picks up reasonable description //$description = $params['amount_level']; // CiviCRM V2.0 - Picks up description $description = $params['description']; $txtOptions = ""; $amountInCents = round(((float) $params['amount']) * 100); $credit_card_name = $params['first_name'] . " "; if (strlen($params['middle_name']) > 0) { $credit_card_name .= $params['middle_name'] . " "; } $credit_card_name .= $params['last_name']; //---------------------------------------------------------------------------------------------------- // We use CiviCRM's param's 'invoiceID' as the unique transaction token to feed to eWAY // Trouble is that eWAY only accepts 16 chars for the token, while CiviCRM's invoiceID is an 32. // As its made from a "$invoiceID = md5(uniqid(rand(), true));" then using the fierst 16 chars // should be alright //---------------------------------------------------------------------------------------------------- $uniqueTrnxNum = substr($params['invoiceID'], 0, 16); //---------------------------------------------------------------------------------------------------- // OPTIONAL: If TEST Card Number force an Override of URL and CutomerID. // During testing CiviCRM once used the LIVE URL. // This code can be uncommented to override the LIVE URL that if CiviCRM does that again. //---------------------------------------------------------------------------------------------------- // if ( ( $gateway_URL == "https://www.eway.com.au/gateway_cvn/xmlpayment.asp") // && ( $params['credit_card_number'] == "4444333322221111" ) ) { // $ewayCustomerID = "87654321"; // $gateway_URL = "https://www.eway.com.au/gateway_cvn/xmltest/testpage.asp"; // } //---------------------------------------------------------------------------------------------------- // Now set the payment details - see http://www.eway.com.au/Support/Developer/PaymentsRealTime.aspx //---------------------------------------------------------------------------------------------------- // 8 Chars - ewayCustomerID - Required $eWAYRequest->EwayCustomerID($ewayCustomerID); // 12 Chars - ewayTotalAmount (in cents) - Required $eWAYRequest->InvoiceAmount($amountInCents); // 50 Chars - ewayCustomerFirstName $eWAYRequest->PurchaserFirstName($params['first_name']); // 50 Chars - ewayCustomerLastName $eWAYRequest->PurchaserLastName($params['last_name']); // 50 Chars - ewayCustomerEmail $eWAYRequest->PurchaserEmailAddress($params['email']); // 255 Chars - ewayCustomerAddress $eWAYRequest->PurchaserAddress($fullAddress); // 6 Chars - ewayCustomerPostcode $eWAYRequest->PurchaserPostalCode($params['postal_code']); // 1000 Chars - ewayCustomerInvoiceDescription $eWAYRequest->InvoiceDescription($description); // 50 Chars - ewayCustomerInvoiceRef $eWAYRequest->InvoiceReference($params['invoiceID']); // 50 Chars - ewayCardHoldersName - Required $eWAYRequest->CardHolderName($credit_card_name); // 20 Chars - ewayCardNumber - Required $eWAYRequest->CardNumber($params['credit_card_number']); // 2 Chars - ewayCardExpiryMonth - Required $eWAYRequest->CardExpiryMonth($expireMonth); // 2 Chars - ewayCardExpiryYear - Required $eWAYRequest->CardExpiryYear($expireYear); // 4 Chars - ewayCVN - Required if CVN Gateway used $eWAYRequest->CVN($params['cvv2']); // 16 Chars - ewayTrxnNumber $eWAYRequest->TransactionNumber($uniqueTrnxNum); // 255 Chars - ewayOption1 $eWAYRequest->EwayOption1($txtOptions); // 255 Chars - ewayOption2 $eWAYRequest->EwayOption2($txtOptions); // 255 Chars - ewayOption3 $eWAYRequest->EwayOption3($txtOptions); $eWAYRequest->CustomerIPAddress($params['ip_address']); $eWAYRequest->CustomerBillingCountry($params['country']); // Allow further manipulation of the arguments via custom hooks .. CRM_Utils_Hook::alterPaymentProcessorParams($this, $params, $eWAYRequest); //---------------------------------------------------------------------------------------------------- // Check to see if we have a duplicate before we send //---------------------------------------------------------------------------------------------------- if ($this->_checkDupe($params['invoiceID'])) { 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 from eWAY. 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.'); } //---------------------------------------------------------------------------------------------------- // Convert to XML and send the payment information //---------------------------------------------------------------------------------------------------- $requestxml = $eWAYRequest->ToXML(); $submit = curl_init($gateway_URL); if (!$submit) { return self::errorExit(9004, 'Could not initiate connection to payment gateway'); } curl_setopt($submit, CURLOPT_POST, TRUE); // return the result on success, FALSE on failure curl_setopt($submit, CURLOPT_RETURNTRANSFER, TRUE); curl_setopt($submit, CURLOPT_POSTFIELDS, $requestxml); curl_setopt($submit, CURLOPT_TIMEOUT, 36000); // if open_basedir or safe_mode are enabled in PHP settings CURLOPT_FOLLOWLOCATION won't work so don't apply it // it's not really required CRM-5841 if (ini_get('open_basedir') == '' && ini_get('safe_mode' == 'Off')) { // ensures any Location headers are followed curl_setopt($submit, CURLOPT_FOLLOWLOCATION, 1); } // Send the data out over the wire //-------------------------------- $responseData = curl_exec($submit); //---------------------------------------------------------------------------------------------------- // See if we had a curl error - if so tell 'em and bail out // // NOTE: curl_error does not return a logical value (see its documentation), but // a string, which is empty when there was no error. //---------------------------------------------------------------------------------------------------- if ((curl_errno($submit) > 0) || (strlen(curl_error($submit)) > 0)) { $errorNum = curl_errno($submit); $errorDesc = curl_error($submit); // Paranoia - in the unlikley event that 'curl' errno fails if ($errorNum == 0) $errorNum = 9005; // Paranoia - in the unlikley event that 'curl' error fails if (strlen($errorDesc) == 0) $errorDesc = "Connection to eWAY payment gateway failed"; return self::errorExit($errorNum, $errorDesc); } //---------------------------------------------------------------------------------------------------- // If null data returned - tell 'em and bail out // // NOTE: You will not necessarily get a string back, if the request failed for // any reason, the return value will be the boolean false. //---------------------------------------------------------------------------------------------------- if (($responseData === FALSE) || (strlen($responseData) == 0)) { return self::errorExit(9006, "Error: Connection to payment gateway failed - no data returned."); } //---------------------------------------------------------------------------------------------------- // If gateway returned no data - tell 'em and bail out //---------------------------------------------------------------------------------------------------- if (empty($responseData)) { return self::errorExit(9007, "Error: No data returned from payment gateway."); } //---------------------------------------------------------------------------------------------------- // Success so far - close the curl and check the data //---------------------------------------------------------------------------------------------------- curl_close($submit); //---------------------------------------------------------------------------------------------------- // Payment successfully sent to gateway - process the response now //---------------------------------------------------------------------------------------------------- $eWAYResponse->ProcessResponse($responseData); //---------------------------------------------------------------------------------------------------- // See if we got an OK result - if not tell 'em and bail out //---------------------------------------------------------------------------------------------------- if (self::isError($eWAYResponse)) { $eWayTrxnError = $eWAYResponse->Error(); CRM_Core_Error::debug_var('eWay Error', $eWayTrxnError, TRUE, TRUE); if (substr($eWayTrxnError, 0, 6) == "Error:") { return self::errorExit(9008, $eWayTrxnError); } $eWayErrorCode = substr($eWayTrxnError, 0, 2); $eWayErrorDesc = substr($eWayTrxnError, 3); return self::errorExit(9008, "Error: [" . $eWayErrorCode . "] - " . $eWayErrorDesc . "."); } //----------------------------------------------------------------------------------------------------- // Cross-Check - the unique 'TrxnReference' we sent out should match the just received 'TrxnReference' // // PLEASE NOTE: If this occurs (which is highly unlikely) its a serious error as it would mean we have // received an OK status from eWAY, but their Gateway has not returned the correct unique // token - ie something is broken, BUT money has been taken from the client's account, // so we can't very well error-out as CiviCRM will then not process the registration. // There is an error message commented out here but my prefered response to this unlikley // possibility is to email 'support@eWAY.com.au' //----------------------------------------------------------------------------------------------------- $eWayTrxnReference_OUT = $eWAYRequest->GetTransactionNumber(); $eWayTrxnReference_IN = $eWAYResponse->InvoiceReference(); if ($eWayTrxnReference_IN != $eWayTrxnReference_OUT) { // return self::errorExit( 9009, "Error: Unique Trxn code was not returned by eWAY Gateway. This is extremely unusual! Please contact the administrator of this site immediately with details of this transaction."); self::send_alert_email($eWAYResponse->TransactionNumber(), $eWayTrxnReference_OUT, $eWayTrxnReference_IN, $requestxml, $responseData ); } /* //---------------------------------------------------------------------------------------------------- // Test mode always returns trxn_id = 0 - so we fix that here // // NOTE: This code was taken from the AuthorizeNet payment processor, however it now appears // unecessary for the eWAY gateway - Left here in case it proves useful //---------------------------------------------------------------------------------------------------- if ( $this->_mode == 'test' ) { $query = "SELECT MAX(trxn_id) FROM civicrm_contribution WHERE trxn_id LIKE 'test%'"; $p = array( ); $trxn_id = strval( CRM_Core_Dao::singleValueQuery( $query, $p ) ); $trxn_id = str_replace( 'test', '', $trxn_id ); $trxn_id = intval($trxn_id) + 1; $params['trxn_id'] = sprintf('test%08d', $trxn_id); } else { $params['trxn_id'] = $eWAYResponse->TransactionNumber(); } */ //============= // Success ! //============= $beaglestatus = $eWAYResponse->BeagleScore(); if (!empty($beaglestatus)) { $beaglestatus = ": " . $beaglestatus; } $params['trxn_result_code'] = $eWAYResponse->Status() . $beaglestatus; $params['gross_amount'] = $eWAYResponse->Amount(); $params['trxn_id'] = $eWAYResponse->TransactionNumber(); return $params; } // end function doDirectPayment /** * Checks to see if invoice_id already exists in db * * @param int $invoiceId * The ID to check. * * @return bool True if ID exists, else false */ public function _checkDupe($invoiceId) { $contribution = new CRM_Contribute_DAO_Contribution(); $contribution->invoice_id = $invoiceId; return $contribution->find(); } /************************************************************************************************* * This function checks the eWAY response status - returning a boolean false if status != 'true' *************************************************************************************************/ public function isError(&$response) { $status = $response->Status(); if ((stripos($status, "true")) === FALSE) { return TRUE; } return FALSE; } /************************************************** * Produces error message and returns from class **************************************************/ 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; } /************************************************** * NOTE: 'doTransferCheckout' not implemented **************************************************/ public function doTransferCheckout(&$params, $component) { CRM_Core_Error::fatal(ts('This function is not implemented')); } /******************************************************************************************** * This public function checks to see if we have the right processor config values set * * NOTE: Called by Events and Contribute to check config params are set prior to trying * register any credit card details * * @return null|string * @internal param string $mode the mode we are operating in (live or test) - not used but could be * to check that the 'test' mode CustomerID was equal to '87654321' and that the URL was * set to https://www.eway.com.au/gateway_cvn/xmltest/TestPage.asp * * returns string $errorMsg if any errors found - null if OK * ****************************************************************************************** */ //function checkConfig( $mode ) // CiviCRM V1.9 Declaration // CiviCRM V2.0 Declaration public function checkConfig() { $errorMsg = array(); if (empty($this->_paymentProcessor['user_name'])) { $errorMsg[] = ts('eWAY CustomerID is not set for this payment processor'); } if (empty($this->_paymentProcessor['url_site'])) { $errorMsg[] = ts('eWAY Gateway URL is not set for this payment processor'); } if (!empty($errorMsg)) { return implode('

', $errorMsg); } else { return NULL; } } /** * @param $p_eWAY_tran_num * @param $p_trxn_out * @param $p_trxn_back * @param $p_request * @param $p_response */ public function send_alert_email($p_eWAY_tran_num, $p_trxn_out, $p_trxn_back, $p_request, $p_response) { // Initialization call is required to use CiviCRM APIs. civicrm_initialize(TRUE); list($fromName, $fromEmail) = CRM_Core_BAO_Domain::getNameAndEmail(); $from = "$fromName <$fromEmail>"; $toName = 'Support at eWAY'; $toEmail = 'Support@eWAY.com.au'; $subject = "ALERT: Unique Trxn Number Failure : eWAY Transaction # = [" . $p_eWAY_tran_num . "]"; $message = " TRXN sent out with request = '$p_trxn_out'. TRXN sent back with response = '$p_trxn_back'. This is a ['$this->_mode'] transaction. Request XML = --------------------------------------------------------------------------- $p_request --------------------------------------------------------------------------- Response XML = --------------------------------------------------------------------------- $p_response --------------------------------------------------------------------------- Regards The CiviCRM eWAY Payment Processor Module "; //$cc = 'Name@Domain'; // create the params array $params = array(); $params['groupName'] = 'eWay Email Sender'; $params['from'] = $from; $params['toName'] = $toName; $params['toEmail'] = $toEmail; $params['subject'] = $subject; $params['cc'] = $cc; $params['text'] = $message; CRM_Utils_Mail::send($params); } } // end class CRM_Core_Payment_eWAY