(NFC) Update CRM/Core CRM/Custom CRM/Dedupe to match the new coder style
[civicrm-core.git] / CRM / Core / Payment / eWAY.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
fee14197 4 | CiviCRM version 5 |
6a488035
TO
5 +--------------------------------------------------------------------+
6 | This file is a part of CiviCRM. |
7 | |
8 | CiviCRM is free software; you can copy, modify, and distribute it |
9 | under the terms of the GNU Affero General Public License |
10 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
11 | |
12 | CiviCRM is distributed in the hope that it will be useful, but |
13 | WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
15 | See the GNU Affero General Public License for more details. |
16 | |
17 | You should have received a copy of the GNU Affero General Public |
18 | License and the CiviCRM Licensing Exception along |
19 | with this program; if not, contact CiviCRM LLC |
20 | at info[AT]civicrm[DOT]org. If you have questions about the |
21 | GNU Affero General Public License or the licensing of CiviCRM, |
22 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
23 +--------------------------------------------------------------------+
d25dd0ee 24 */
6a488035
TO
25
26
27/*
28 +--------------------------------------------------------------------+
775788cf 29 | eWAY Core Payment Module for CiviCRM version 5 & 1.9 |
6a488035
TO
30 +--------------------------------------------------------------------+
31 | Licensed to CiviCRM under the Academic Free License version 3.0 |
32 | |
33 | Written & Contributed by Dolphin Software P/L - March 2008 |
34 +--------------------------------------------------------------------+
35 | |
36 | This file is a part of CiviCRM. |
37 | |
38 | This code was initially based on the recent PayJunction module |
39 | contributed by Phase2 Technology, and then plundered bits from |
40 | the AuthorizeNet module contributed by Ideal Solution, and |
41 | referenced the eWAY code in Drupal 5.7's ecommerce-5.x-3.4 and |
42 | ecommerce-5.x-4.x-dev modules. |
43 | |
44 | Plus a bit of our own code of course - Peter Barwell |
45 | contact PB@DolphinSoftware.com.au if required. |
46 | |
b44e3f84 47 | NOTE: This initial eWAY module does not yet allow for recurring |
6a488035
TO
48 | payments - contact Peter Barwell or add yourself (or both) |
49 | |
50 | NOTE: The eWAY gateway only allows a single currency per account |
51 | (per eWAY CustomerID) ie you can only have one currency per |
52 | added Payment Processor. |
53 | The only way to add multi-currency is to code it so that a |
54 | different CustomerID is used per currency. |
55 | |
56 +--------------------------------------------------------------------+
e70a7fc0 57 */
6a488035
TO
58
59/**
353ffa53
TO
60 * -----------------------------------------------------------------------------------------------
61 * From the eWAY supplied 'Web.config' dated 25-Sep-2006 - check date and update links if required
62 * -----------------------------------------------------------------------------------------------
63 *
64 * LIVE gateway with CVN
65 * https://www.eway.com.au/gateway_cvn/xmlpayment.asp
66 *
67 * LIVE gateway without CVN
68 * https://www.eway.com.au/gateway/xmlpayment.asp
69 *
70 *
71 * Test gateway with CVN
72 * https://www.eway.com.au/gateway_cvn/xmltest/TestPage.asp
73 *
74 * Test gateway without CVN
75 * https://www.eway.com.au/gateway/xmltest/TestPage.asp
76 *
77 *
78 * LIVE gateway for Stored Transactions
79 * https://www.eway.com.au/gateway/xmlstored.asp
80 *
81 *
82 * -----------------------------------------------------------------------------------------------
83 * From the eWAY web-site - http://www.eway.com.au/Support/Developer/PaymentsRealTime.aspx
84 * -----------------------------------------------------------------------------------------------
85 * The test Customer ID is 87654321 - this is the only ID that will work on the test gateway.
86 * The test Credit Card number is 4444333322221111
87 * - this is the only credit card number that will work on the test gateway.
88 * The test Total Amount should end in 00 or 08 to get a successful response (e.g. $10.00 or $10.08)
89 * ie - all other amounts will return a failed response.
90 *
91 * -----------------------------------------------------------------------------------------------
c301f76e 92 */
404d48bf 93
94// require Standard eWAY API libraries
95require_once 'eWAY/eWAY_GatewayRequest.php';
96require_once 'eWAY/eWAY_GatewayResponse.php';
97
8246bca4 98/**
99 * Class CRM_Core_Payment_eWAY.
100 */
6a488035
TO
101class CRM_Core_Payment_eWAY extends CRM_Core_Payment {
102 # (not used, implicit in the API, might need to convert?)
7da04cde 103 const CHARSET = 'UTF-8';
6a488035
TO
104
105 /**
106 * We only need one instance of this object. So we use the singleton
107 * pattern and cache the instance in this variable
108 *
109 * @var object
6a488035
TO
110 */
111 static private $_singleton = NULL;
112
c301f76e 113 /**
114 * *******************************************************
6a488035
TO
115 * Constructor
116 *
6a0b768e
TO
117 * @param string $mode
118 * The mode of operation: live or test.
6a488035 119 *
c301f76e 120 * @param int $paymentProcessor
dd244018 121 *
c301f76e 122 * *******************************************************
dd244018 123 */
00be9182 124 public function __construct($mode, &$paymentProcessor) {
6a488035
TO
125
126 // live or test
127 $this->_mode = $mode;
128 $this->_paymentProcessor = $paymentProcessor;
129 $this->_processorName = ts('eWay');
130 }
131
c301f76e 132 /**
ad37ac8e 133 * Sends request and receive response from eWAY payment process.
134 *
135 * @param array $params
136 *
137 * @return array|object
138 * @throws \Exception
c301f76e 139 */
00be9182 140 public function doDirectPayment(&$params) {
d597ad57 141 if (CRM_Utils_Array::value('is_recur', $params) == TRUE) {
6a488035
TO
142 CRM_Core_Error::fatal(ts('eWAY - recurring payments not implemented'));
143 }
144
145 if (!defined('CURLOPT_SSLCERT')) {
146 CRM_Core_Error::fatal(ts('eWAY - Gateway requires curl with SSL support'));
147 }
148
149 // eWAY Client ID
150 $ewayCustomerID = $this->_paymentProcessor['user_name'];
151 // eWAY Gateway URL
152 $gateway_URL = $this->_paymentProcessor['url_site'];
153
154 //------------------------------------
155 // create eWAY gateway objects
156 //------------------------------------
c301f76e 157 $eWAYRequest = new GatewayRequest();
6a488035
TO
158
159 if (($eWAYRequest == NULL) || (!($eWAYRequest instanceof GatewayRequest))) {
160 return self::errorExit(9001, "Error: Unable to create eWAY Request object.");
161 }
162
c301f76e 163 $eWAYResponse = new GatewayResponse();
6a488035
TO
164
165 if (($eWAYResponse == NULL) || (!($eWAYResponse instanceof GatewayResponse))) {
166 return self::errorExit(9002, "Error: Unable to create eWAY Response object.");
167 }
168
169 /*
e70a7fc0
TO
170 //-------------------------------------------------------------
171 // NOTE: eWAY Doesn't use the following at the moment:
172 //-------------------------------------------------------------
173 $creditCardType = $params['credit_card_type'];
174 $currentcyID = $params['currencyID'];
175 $country = $params['country'];
176 */
6a488035 177
6a488035
TO
178 //-------------------------------------------------------------
179 // Prepare some composite data from _paymentProcessor fields
180 //-------------------------------------------------------------
181 $fullAddress = $params['street_address'] . ", " . $params['city'] . ", " . $params['state_province'] . ".";
353ffa53 182 $expireYear = substr($params['year'], 2, 2);
6a488035
TO
183 $expireMonth = sprintf('%02d', (int) $params['month']);
184 // CiviCRM V1.9 - Picks up reasonable description
185 //$description = $params['amount_level'];
186 // CiviCRM V2.0 - Picks up description
187 $description = $params['description'];
188 $txtOptions = "";
189
190 $amountInCents = round(((float) $params['amount']) * 100);
191
192 $credit_card_name = $params['first_name'] . " ";
193 if (strlen($params['middle_name']) > 0) {
194 $credit_card_name .= $params['middle_name'] . " ";
195 }
196 $credit_card_name .= $params['last_name'];
197
198 //----------------------------------------------------------------------------------------------------
199 // We use CiviCRM's param's 'invoiceID' as the unique transaction token to feed to eWAY
200 // Trouble is that eWAY only accepts 16 chars for the token, while CiviCRM's invoiceID is an 32.
201 // As its made from a "$invoiceID = md5(uniqid(rand(), true));" then using the fierst 16 chars
202 // should be alright
203 //----------------------------------------------------------------------------------------------------
204 $uniqueTrnxNum = substr($params['invoiceID'], 0, 16);
205
206 //----------------------------------------------------------------------------------------------------
207 // OPTIONAL: If TEST Card Number force an Override of URL and CutomerID.
208 // During testing CiviCRM once used the LIVE URL.
209 // This code can be uncommented to override the LIVE URL that if CiviCRM does that again.
210 //----------------------------------------------------------------------------------------------------
211 // if ( ( $gateway_URL == "https://www.eway.com.au/gateway_cvn/xmlpayment.asp")
212 // && ( $params['credit_card_number'] == "4444333322221111" ) ) {
213 // $ewayCustomerID = "87654321";
214 // $gateway_URL = "https://www.eway.com.au/gateway_cvn/xmltest/testpage.asp";
215 // }
216
217 //----------------------------------------------------------------------------------------------------
218 // Now set the payment details - see http://www.eway.com.au/Support/Developer/PaymentsRealTime.aspx
219 //----------------------------------------------------------------------------------------------------
220 // 8 Chars - ewayCustomerID - Required
221 $eWAYRequest->EwayCustomerID($ewayCustomerID);
222 // 12 Chars - ewayTotalAmount (in cents) - Required
223 $eWAYRequest->InvoiceAmount($amountInCents);
224 // 50 Chars - ewayCustomerFirstName
225 $eWAYRequest->PurchaserFirstName($params['first_name']);
226 // 50 Chars - ewayCustomerLastName
227 $eWAYRequest->PurchaserLastName($params['last_name']);
228 // 50 Chars - ewayCustomerEmail
229 $eWAYRequest->PurchaserEmailAddress($params['email']);
230 // 255 Chars - ewayCustomerAddress
231 $eWAYRequest->PurchaserAddress($fullAddress);
232 // 6 Chars - ewayCustomerPostcode
233 $eWAYRequest->PurchaserPostalCode($params['postal_code']);
234 // 1000 Chars - ewayCustomerInvoiceDescription
235 $eWAYRequest->InvoiceDescription($description);
236 // 50 Chars - ewayCustomerInvoiceRef
237 $eWAYRequest->InvoiceReference($params['invoiceID']);
238 // 50 Chars - ewayCardHoldersName - Required
239 $eWAYRequest->CardHolderName($credit_card_name);
240 // 20 Chars - ewayCardNumber - Required
241 $eWAYRequest->CardNumber($params['credit_card_number']);
242 // 2 Chars - ewayCardExpiryMonth - Required
243 $eWAYRequest->CardExpiryMonth($expireMonth);
244 // 2 Chars - ewayCardExpiryYear - Required
245 $eWAYRequest->CardExpiryYear($expireYear);
246 // 4 Chars - ewayCVN - Required if CVN Gateway used
247 $eWAYRequest->CVN($params['cvv2']);
248 // 16 Chars - ewayTrxnNumber
249 $eWAYRequest->TransactionNumber($uniqueTrnxNum);
250 // 255 Chars - ewayOption1
251 $eWAYRequest->EwayOption1($txtOptions);
252 // 255 Chars - ewayOption2
253 $eWAYRequest->EwayOption2($txtOptions);
254 // 255 Chars - ewayOption3
255 $eWAYRequest->EwayOption3($txtOptions);
256
257 $eWAYRequest->CustomerIPAddress($params['ip_address']);
258 $eWAYRequest->CustomerBillingCountry($params['country']);
259
260 // Allow further manipulation of the arguments via custom hooks ..
261 CRM_Utils_Hook::alterPaymentProcessorParams($this, $params, $eWAYRequest);
262
263 //----------------------------------------------------------------------------------------------------
264 // Check to see if we have a duplicate before we send
265 //----------------------------------------------------------------------------------------------------
d253aeb8 266 if ($this->checkDupe($params['invoiceID'], CRM_Utils_Array::value('contributionID', $params))) {
6a488035
TO
267 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.');
268 }
269
270 //----------------------------------------------------------------------------------------------------
271 // Convert to XML and send the payment information
272 //----------------------------------------------------------------------------------------------------
273 $requestxml = $eWAYRequest->ToXML();
274
275 $submit = curl_init($gateway_URL);
276
277 if (!$submit) {
278 return self::errorExit(9004, 'Could not initiate connection to payment gateway');
279 }
280
281 curl_setopt($submit, CURLOPT_POST, TRUE);
282 // return the result on success, FALSE on failure
283 curl_setopt($submit, CURLOPT_RETURNTRANSFER, TRUE);
284 curl_setopt($submit, CURLOPT_POSTFIELDS, $requestxml);
285 curl_setopt($submit, CURLOPT_TIMEOUT, 36000);
286 // if open_basedir or safe_mode are enabled in PHP settings CURLOPT_FOLLOWLOCATION won't work so don't apply it
287 // it's not really required CRM-5841
288 if (ini_get('open_basedir') == '' && ini_get('safe_mode' == 'Off')) {
289 // ensures any Location headers are followed
290 curl_setopt($submit, CURLOPT_FOLLOWLOCATION, 1);
291 }
292
293 // Send the data out over the wire
294 //--------------------------------
295 $responseData = curl_exec($submit);
296
297 //----------------------------------------------------------------------------------------------------
298 // See if we had a curl error - if so tell 'em and bail out
299 //
300 // NOTE: curl_error does not return a logical value (see its documentation), but
301 // a string, which is empty when there was no error.
302 //----------------------------------------------------------------------------------------------------
303 if ((curl_errno($submit) > 0) || (strlen(curl_error($submit)) > 0)) {
304 $errorNum = curl_errno($submit);
305 $errorDesc = curl_error($submit);
306
307 // Paranoia - in the unlikley event that 'curl' errno fails
2aa397bc
TO
308 if ($errorNum == 0) {
309 $errorNum = 9005;
310 }
6a488035
TO
311
312 // Paranoia - in the unlikley event that 'curl' error fails
2aa397bc
TO
313 if (strlen($errorDesc) == 0) {
314 $errorDesc = "Connection to eWAY payment gateway failed";
315 }
6a488035
TO
316
317 return self::errorExit($errorNum, $errorDesc);
318 }
319
320 //----------------------------------------------------------------------------------------------------
321 // If null data returned - tell 'em and bail out
322 //
323 // NOTE: You will not necessarily get a string back, if the request failed for
324 // any reason, the return value will be the boolean false.
325 //----------------------------------------------------------------------------------------------------
326 if (($responseData === FALSE) || (strlen($responseData) == 0)) {
327 return self::errorExit(9006, "Error: Connection to payment gateway failed - no data returned.");
328 }
329
330 //----------------------------------------------------------------------------------------------------
331 // If gateway returned no data - tell 'em and bail out
332 //----------------------------------------------------------------------------------------------------
333 if (empty($responseData)) {
334 return self::errorExit(9007, "Error: No data returned from payment gateway.");
335 }
336
337 //----------------------------------------------------------------------------------------------------
338 // Success so far - close the curl and check the data
339 //----------------------------------------------------------------------------------------------------
340 curl_close($submit);
341
342 //----------------------------------------------------------------------------------------------------
ceb10dc7 343 // Payment successfully sent to gateway - process the response now
6a488035
TO
344 //----------------------------------------------------------------------------------------------------
345 $eWAYResponse->ProcessResponse($responseData);
346
347 //----------------------------------------------------------------------------------------------------
348 // See if we got an OK result - if not tell 'em and bail out
349 //----------------------------------------------------------------------------------------------------
350 if (self::isError($eWAYResponse)) {
351 $eWayTrxnError = $eWAYResponse->Error();
09e49db4 352 CRM_Core_Error::debug_var('eWay Error', $eWayTrxnError, TRUE, TRUE);
6a488035
TO
353 if (substr($eWayTrxnError, 0, 6) == "Error:") {
354 return self::errorExit(9008, $eWayTrxnError);
355 }
356 $eWayErrorCode = substr($eWayTrxnError, 0, 2);
357 $eWayErrorDesc = substr($eWayTrxnError, 3);
358
359 return self::errorExit(9008, "Error: [" . $eWayErrorCode . "] - " . $eWayErrorDesc . ".");
360 }
361
362 //-----------------------------------------------------------------------------------------------------
363 // Cross-Check - the unique 'TrxnReference' we sent out should match the just received 'TrxnReference'
364 //
365 // PLEASE NOTE: If this occurs (which is highly unlikely) its a serious error as it would mean we have
366 // received an OK status from eWAY, but their Gateway has not returned the correct unique
367 // token - ie something is broken, BUT money has been taken from the client's account,
368 // so we can't very well error-out as CiviCRM will then not process the registration.
b44e3f84 369 // There is an error message commented out here but my preferred response to this unlikley
6a488035
TO
370 // possibility is to email 'support@eWAY.com.au'
371 //-----------------------------------------------------------------------------------------------------
372 $eWayTrxnReference_OUT = $eWAYRequest->GetTransactionNumber();
373 $eWayTrxnReference_IN = $eWAYResponse->InvoiceReference();
374
375 if ($eWayTrxnReference_IN != $eWayTrxnReference_OUT) {
376 // 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.");
377
6a488035
TO
378 }
379
380 /*
e70a7fc0
TO
381 //----------------------------------------------------------------------------------------------------
382 // Test mode always returns trxn_id = 0 - so we fix that here
383 //
384 // NOTE: This code was taken from the AuthorizeNet payment processor, however it now appears
b44e3f84 385 // unnecessary for the eWAY gateway - Left here in case it proves useful
e70a7fc0
TO
386 //----------------------------------------------------------------------------------------------------
387 if ( $this->_mode == 'test' ) {
388 $query = "SELECT MAX(trxn_id) FROM civicrm_contribution WHERE trxn_id LIKE 'test%'";
389 $p = array( );
390 $trxn_id = strval( CRM_Core_Dao::singleValueQuery( $query, $p ) );
391 $trxn_id = str_replace( 'test', '', $trxn_id );
392 $trxn_id = intval($trxn_id) + 1;
393 $params['trxn_id'] = sprintf('test%08d', $trxn_id);
394 }
395 else {
396 $params['trxn_id'] = $eWAYResponse->TransactionNumber();
397 }
398 */
6a488035 399
6a488035
TO
400 //=============
401 // Success !
402 //=============
403 $beaglestatus = $eWAYResponse->BeagleScore();
404 if (!empty($beaglestatus)) {
405 $beaglestatus = ": " . $beaglestatus;
406 }
407 $params['trxn_result_code'] = $eWAYResponse->Status() . $beaglestatus;
408 $params['gross_amount'] = $eWAYResponse->Amount();
409 $params['trxn_id'] = $eWAYResponse->TransactionNumber();
410
411 return $params;
412 }
518fa0ee 413
6a488035
TO
414 // end function doDirectPayment
415
c301f76e 416 /**
ad37ac8e 417 * Checks the eWAY response status - returning a boolean false if status != 'true'.
418 *
419 * @param object $response
420 *
421 * @return bool
c301f76e 422 */
00be9182 423 public function isError(&$response) {
6a488035
TO
424 $status = $response->Status();
425
426 if ((stripos($status, "true")) === FALSE) {
427 return TRUE;
428 }
429 return FALSE;
430 }
431
c301f76e 432 /**
ea3ddccf 433 * Produces error message and returns from class.
434 *
435 * @param int $errorCode
436 * @param string $errorMessage
437 *
438 * @return object
c301f76e 439 */
00be9182 440 public function &errorExit($errorCode = NULL, $errorMessage = NULL) {
6a488035
TO
441 $e = CRM_Core_Error::singleton();
442
443 if ($errorCode) {
444 $e->push($errorCode, 0, NULL, $errorMessage);
445 }
446 else {
447 $e->push(9000, 0, NULL, 'Unknown System Error.');
448 }
449 return $e;
450 }
451
c301f76e 452 /**
453 * *****************************************************************************************
6a488035
TO
454 * This public function checks to see if we have the right processor config values set
455 *
456 * NOTE: Called by Events and Contribute to check config params are set prior to trying
457 * register any credit card details
458 *
77b97be7
EM
459 * @return null|string
460 * @internal param string $mode the mode we are operating in (live or test) - not used but could be
6a488035
TO
461 * to check that the 'test' mode CustomerID was equal to '87654321' and that the URL was
462 * set to https://www.eway.com.au/gateway_cvn/xmltest/TestPage.asp
463 *
464 * returns string $errorMsg if any errors found - null if OK
465 *
c301f76e 466 * *****************************************************************************************
77b97be7 467 */
00be9182 468 public function checkConfig() {
be2fb01f 469 $errorMsg = [];
6a488035
TO
470
471 if (empty($this->_paymentProcessor['user_name'])) {
472 $errorMsg[] = ts('eWAY CustomerID is not set for this payment processor');
473 }
474
475 if (empty($this->_paymentProcessor['url_site'])) {
476 $errorMsg[] = ts('eWAY Gateway URL is not set for this payment processor');
477 }
478
479 if (!empty($errorMsg)) {
480 return implode('<p>', $errorMsg);
481 }
482 else {
483 return NULL;
484 }
485 }
486
6a488035
TO
487}
488// end class CRM_Core_Payment_eWAY