Merge pull request #3208 from eileenmcnaughton/comments
[civicrm-core.git] / CRM / Core / Payment / AuthorizeNet.php
CommitLineData
6a488035
TO
1<?php
2/*
3 * Copyright (C) 2007
4 * Licensed to CiviCRM under the Academic Free License version 3.0.
5 *
6 * Written and contributed by Ideal Solution, LLC (http://www.idealso.com)
7 *
8 */
9
10/**
11 *
12 * @package CRM
13 * @author Marshal Newrock <marshal@idealso.com>
14 */
15
16/* NOTE:
17 * When looking up response codes in the Authorize.Net API, they
18 * begin at one, so always delete one from the "Position in Response"
19 */
20class CRM_Core_Payment_AuthorizeNet extends CRM_Core_Payment {
21 CONST CHARSET = 'iso-8859-1';
22 CONST AUTH_APPROVED = 1;
23 CONST AUTH_DECLINED = 2;
24 CONST AUTH_ERROR = 3;
25 CONST TIMEZONE = 'America/Denver';
26
27 protected $_mode = NULL;
28
29 protected $_params = array();
30
31 /**
32 * We only need one instance of this object. So we use the singleton
33 * pattern and cache the instance in this variable
34 *
35 * @var object
36 * @static
37 */
38 static private $_singleton = NULL;
39
40 /**
41 * Constructor
42 *
43 * @param string $mode the mode of operation: live or test
44 *
45 * @return void
46 */
47 function __construct($mode, &$paymentProcessor) {
48 $this->_mode = $mode;
49 $this->_paymentProcessor = $paymentProcessor;
50 $this->_processorName = ts('Authorize.net');
51
52 $config = CRM_Core_Config::singleton();
53 $this->_setParam('apiLogin', $paymentProcessor['user_name']);
54 $this->_setParam('paymentKey', $paymentProcessor['password']);
55 $this->_setParam('paymentType', 'AIM');
56 $this->_setParam('md5Hash', $paymentProcessor['signature']);
57
58 $this->_setParam('emailCustomer', 'TRUE');
59 $this->_setParam('timestamp', time());
60 srand(time());
61 $this->_setParam('sequence', rand(1, 1000));
62 }
63
64 /**
65 * singleton function used to manage this object
66 *
67 * @param string $mode the mode of operation: live or test
68 * @param object $paymentProcessor the details of the payment processor being invoked
69 * @param object $paymentForm reference to the form object if available
70 * @param boolean $force should we force a reload of this payment object
71 *
72 * @return object
73 * @static
74 *
75 */
76 static function &singleton($mode, &$paymentProcessor, &$paymentForm = NULL, $force = FALSE) {
77 $processorName = $paymentProcessor['name'];
78 if (!isset(self::$_singleton[$processorName]) || self::$_singleton[$processorName] === NULL) {
79 self::$_singleton[$processorName] = new CRM_Core_Payment_AuthorizeNet($mode, $paymentProcessor);
80 }
81 return self::$_singleton[$processorName];
82 }
83
84 /**
85 * Submit a payment using Advanced Integration Method
86 *
87 * @param array $params assoc array of input parameters for this transaction
88 *
89 * @return array the result in a nice formatted array (or an error object)
90 * @public
91 */
92 function doDirectPayment(&$params) {
93 if (!defined('CURLOPT_SSLCERT')) {
94 return self::error(9001, 'Authorize.Net requires curl with SSL support');
95 }
96
97 /*
98 * recurpayment function does not compile an array & then proces it -
99 * - the tpl does the transformation so adding call to hook here
100 * & giving it a change to act on the params array
101 */
102 $newParams = $params;
cd125a40 103 if (!empty($params['is_recur']) && !empty($params['contributionRecurID'])) {
6a488035
TO
104 CRM_Utils_Hook::alterPaymentProcessorParams($this,
105 $params,
106 $newParams
107 );
108 }
109 foreach ($newParams as $field => $value) {
110 $this->_setParam($field, $value);
111 }
112
cd125a40 113 if (!empty($params['is_recur']) && !empty($params['contributionRecurID'])) {
6a488035
TO
114 $result = $this->doRecurPayment();
115 if (is_a($result, 'CRM_Core_Error')) {
116 return $result;
117 }
118 return $params;
119 }
120
121 $postFields = array();
122 $authorizeNetFields = $this->_getAuthorizeNetFields();
123
124 // Set up our call for hook_civicrm_paymentProcessor,
125 // since we now have our parameters as assigned for the AIM back end.
126 CRM_Utils_Hook::alterPaymentProcessorParams($this,
127 $params,
128 $authorizeNetFields
129 );
130
131 foreach ($authorizeNetFields as $field => $value) {
132 // CRM-7419, since double quote is used as enclosure while doing csv parsing
133 $value = ($field == 'x_description') ? str_replace('"', "'", $value) : $value;
134 $postFields[] = $field . '=' . urlencode($value);
135 }
136
137 // Authorize.Net will not refuse duplicates, so we should check if the user already submitted this transaction
138 if ($this->_checkDupe($authorizeNetFields['x_invoice_num'])) {
139 return self::error(9004, '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 Authorize.net. 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.');
140 }
141
142 $submit = curl_init($this->_paymentProcessor['url_site']);
143
144 if (!$submit) {
145 return self::error(9002, 'Could not initiate connection to payment gateway');
146 }
147
148 curl_setopt($submit, CURLOPT_POST, TRUE);
149 curl_setopt($submit, CURLOPT_RETURNTRANSFER, TRUE);
150 curl_setopt($submit, CURLOPT_POSTFIELDS, implode('&', $postFields));
151 curl_setopt($submit, CURLOPT_SSL_VERIFYPEER, CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'verifySSL'));
152
153 $response = curl_exec($submit);
154
155 if (!$response) {
156 return self::error(curl_errno($submit), curl_error($submit));
157 }
158
159 curl_close($submit);
160
161 $response_fields = $this->explode_csv($response);
162
163 // check gateway MD5 response
164 if (!$this->checkMD5($response_fields[37], $response_fields[6], $response_fields[9])) {
165 return self::error(9003, 'MD5 Verification failed');
166 }
167
168 // check for application errors
169 // TODO:
170 // AVS, CVV2, CAVV, and other verification results
171 if ($response_fields[0] != self::AUTH_APPROVED) {
172 $errormsg = $response_fields[2] . ' ' . $response_fields[3];
173 return self::error($response_fields[1], $errormsg);
174 }
175
176 // Success
177
178 // test mode always returns trxn_id = 0
179 // also live mode in CiviCRM with test mode set in
180 // Authorize.Net return $response_fields[6] = 0
181 // hence treat that also as test mode transaction
182 // fix for CRM-2566
183 if (($this->_mode == 'test') || $response_fields[6] == 0) {
c3e297c4 184 $query = "SELECT MAX(trxn_id) FROM civicrm_contribution WHERE trxn_id RLIKE 'test[0-9]+'";
6a488035
TO
185 $p = array();
186 $trxn_id = strval(CRM_Core_Dao::singleValueQuery($query, $p));
187 $trxn_id = str_replace('test', '', $trxn_id);
188 $trxn_id = intval($trxn_id) + 1;
189 $params['trxn_id'] = sprintf('test%08d', $trxn_id);
190 }
191 else {
192 $params['trxn_id'] = $response_fields[6];
193 }
194 $params['gross_amount'] = $response_fields[9];
195 // TODO: include authorization code?
196
197 return $params;
198 }
199
200 /**
201 * Submit an Automated Recurring Billing subscription
202 *
203 * @public
204 */
205 function doRecurPayment() {
206 $template = CRM_Core_Smarty::singleton();
207
208 $intervalLength = $this->_getParam('frequency_interval');
209 $intervalUnit = $this->_getParam('frequency_unit');
210 if ($intervalUnit == 'week') {
211 $intervalLength *= 7;
212 $intervalUnit = 'days';
213 }
214 elseif ($intervalUnit == 'year') {
215 $intervalLength *= 12;
216 $intervalUnit = 'months';
217 }
218 elseif ($intervalUnit == 'day') {
219 $intervalUnit = 'days';
220 }
221 elseif ($intervalUnit == 'month') {
222 $intervalUnit = 'months';
223 }
224
225 // interval cannot be less than 7 days or more than 1 year
226 if ($intervalUnit == 'days') {
227 if ($intervalLength < 7) {
228 return self::error(9001, 'Payment interval must be at least one week');
229 }
230 elseif ($intervalLength > 365) {
231 return self::error(9001, 'Payment interval may not be longer than one year');
232 }
233 }
234 elseif ($intervalUnit == 'months') {
235 if ($intervalLength < 1) {
236 return self::error(9001, 'Payment interval must be at least one week');
237 }
238 elseif ($intervalLength > 12) {
239 return self::error(9001, 'Payment interval may not be longer than one year');
240 }
241 }
242
243 $template->assign('intervalLength', $intervalLength);
244 $template->assign('intervalUnit', $intervalUnit);
245
246 $template->assign('apiLogin', $this->_getParam('apiLogin'));
247 $template->assign('paymentKey', $this->_getParam('paymentKey'));
248 $template->assign('refId', substr($this->_getParam('invoiceID'), 0, 20));
249
250 //for recurring, carry first contribution id
251 $template->assign('invoiceNumber', $this->_getParam('contributionID'));
252 $firstPaymentDate = $this->_getParam('receive_date');
253 if (!empty($firstPaymentDate)) {
254 //allow for post dated payment if set in form
255 $startDate = date_create($firstPaymentDate);
256 }
257 else {
258 $startDate = date_create();
259 }
260 /* Format start date in Mountain Time to avoid Authorize.net error E00017
261 * we do this only if the day we are setting our start time to is LESS than the current
262 * day in mountaintime (ie. the server time of the A-net server). A.net won't accept a date
263 * earlier than the current date on it's server so if we are in PST we might need to use mountain
264 * time to bring our date forward. But if we are submitting something future dated we want
265 * the date we entered to be respected
266 */
267 $minDate = date_create('now', new DateTimeZone(self::TIMEZONE));
268 if(strtotime($startDate->format('Y-m-d')) < strtotime($minDate->format('Y-m-d'))){
269 $startDate->setTimezone(new DateTimeZone(self::TIMEZONE));
270 }
271
272 $template->assign( 'startDate', $startDate->format('Y-m-d') );
a2dd09cc 273
6a488035 274 $installments = $this->_getParam('installments');
a2dd09cc
DL
275
276 // for open ended subscription totalOccurrences has to be 9999
277 $installments = empty($installments) ? 9999 : $installments;
278 $template->assign('totalOccurrences', $installments);
6a488035
TO
279
280 $template->assign('amount', $this->_getParam('amount'));
281
282 $template->assign('cardNumber', $this->_getParam('credit_card_number'));
283 $exp_month = str_pad($this->_getParam('month'), 2, '0', STR_PAD_LEFT);
284 $exp_year = $this->_getParam('year');
285 $template->assign('expirationDate', $exp_year . '-' . $exp_month);
286
287 // name rather than description is used in the tpl - see http://www.authorize.net/support/ARB_guide.pdf
288 $template->assign('name', $this->_getParam('description', TRUE));
289
290 $template->assign('email', $this->_getParam('email'));
291 $template->assign('contactID', $this->_getParam('contactID'));
292 $template->assign('billingFirstName', $this->_getParam('billing_first_name'));
293 $template->assign('billingLastName', $this->_getParam('billing_last_name'));
294 $template->assign('billingAddress', $this->_getParam('street_address', TRUE));
295 $template->assign('billingCity', $this->_getParam('city', TRUE));
296 $template->assign('billingState', $this->_getParam('state_province'));
297 $template->assign('billingZip', $this->_getParam('postal_code', TRUE));
298 $template->assign('billingCountry', $this->_getParam('country'));
299
300 $arbXML = $template->fetch('CRM/Contribute/Form/Contribution/AuthorizeNetARB.tpl');
301 // submit to authorize.net
302
303 $submit = curl_init($this->_paymentProcessor['url_recur']);
304 if (!$submit) {
305 return self::error(9002, 'Could not initiate connection to payment gateway');
306 }
307 curl_setopt($submit, CURLOPT_RETURNTRANSFER, 1);
308 curl_setopt($submit, CURLOPT_HTTPHEADER, array("Content-Type: text/xml"));
309 curl_setopt($submit, CURLOPT_HEADER, 1);
310 curl_setopt($submit, CURLOPT_POSTFIELDS, $arbXML);
311 curl_setopt($submit, CURLOPT_POST, 1);
312 curl_setopt($submit, CURLOPT_SSL_VERIFYPEER, CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'verifySSL'));
313
314 $response = curl_exec($submit);
315
316 if (!$response) {
317 return self::error(curl_errno($submit), curl_error($submit));
318 }
319
320 curl_close($submit);
321 $responseFields = $this->_ParseArbReturn($response);
322
323 if ($responseFields['resultCode'] == 'Error') {
324 return self::error($responseFields['code'], $responseFields['text']);
325 }
326
327 // update recur processor_id with subscriptionId
328 CRM_Core_DAO::setFieldValue('CRM_Contribute_DAO_ContributionRecur', $this->_getParam('contributionRecurID'),
329 'processor_id', $responseFields['subscriptionId']
330 );
331 //only impact of assigning this here is is can be used to cancel the subscription in an automated test
332 // if it isn't cancelled a duplicate transaction error occurs
a7488080 333 if (!empty($responseFields['subscriptionId'])) {
6a488035
TO
334 $this->_setParam('subscriptionId', $responseFields['subscriptionId']);
335 }
336 }
337
338 function _getAuthorizeNetFields() {
339 $amount = $this->_getParam('total_amount');//Total amount is from the form contribution field
340 if(empty($amount)){//CRM-9894 would this ever be the case??
341 $amount = $this->_getParam('amount');
342 }
343 $fields = array();
344 $fields['x_login'] = $this->_getParam('apiLogin');
345 $fields['x_tran_key'] = $this->_getParam('paymentKey');
346 $fields['x_email_customer'] = $this->_getParam('emailCustomer');
347 $fields['x_first_name'] = $this->_getParam('billing_first_name');
348 $fields['x_last_name'] = $this->_getParam('billing_last_name');
349 $fields['x_address'] = $this->_getParam('street_address');
350 $fields['x_city'] = $this->_getParam('city');
351 $fields['x_state'] = $this->_getParam('state_province');
352 $fields['x_zip'] = $this->_getParam('postal_code');
353 $fields['x_country'] = $this->_getParam('country');
354 $fields['x_customer_ip'] = $this->_getParam('ip_address');
355 $fields['x_email'] = $this->_getParam('email');
356 $fields['x_invoice_num'] = substr($this->_getParam('invoiceID'), 0, 20);
357 $fields['x_amount'] = $amount;
358 $fields['x_currency_code'] = $this->_getParam('currencyID');
359 $fields['x_description'] = $this->_getParam('description');
360 $fields['x_cust_id'] = $this->_getParam('contactID');
361 if ($this->_getParam('paymentType') == 'AIM') {
362 $fields['x_relay_response'] = 'FALSE';
363 // request response in CSV format
364 $fields['x_delim_data'] = 'TRUE';
365 $fields['x_delim_char'] = ',';
366 $fields['x_encap_char'] = '"';
367 // cc info
368 $fields['x_card_num'] = $this->_getParam('credit_card_number');
369 $fields['x_card_code'] = $this->_getParam('cvv2');
370 $exp_month = str_pad($this->_getParam('month'), 2, '0', STR_PAD_LEFT);
371 $exp_year = $this->_getParam('year');
372 $fields['x_exp_date'] = "$exp_month/$exp_year";
373 }
374
375 if ($this->_mode != 'live') {
376 $fields['x_test_request'] = 'TRUE';
377 }
378
379 return $fields;
380 }
381
382 /**
383 * Checks to see if invoice_id already exists in db
384 *
385 * @param int $invoiceId The ID to check
386 *
387 * @return bool True if ID exists, else false
388 */
389 function _checkDupe($invoiceId) {
390 $contribution = new CRM_Contribute_DAO_Contribution();
391 $contribution->invoice_id = $invoiceId;
392 return $contribution->find();
393 }
394
395 /**
396 * Generate HMAC_MD5
397 *
398 * @param string $key
399 * @param string $data
400 *
401 * @return string the HMAC_MD5 encoding string
402 **/
403 function hmac($key, $data) {
404 if (function_exists('mhash')) {
405 // Use PHP mhash extension
406 return (bin2hex(mhash(MHASH_MD5, $data, $key)));
407 }
408 else {
409 // RFC 2104 HMAC implementation for php.
410 // Creates an md5 HMAC.
411 // Eliminates the need to install mhash to compute a HMAC
412 // Hacked by Lance Rushing
413 // byte length for md5
414 $b = 64;
415 if (strlen($key) > $b) {
416 $key = pack("H*", md5($key));
417 }
418 $key = str_pad($key, $b, chr(0x00));
419 $ipad = str_pad('', $b, chr(0x36));
420 $opad = str_pad('', $b, chr(0x5c));
421 $k_ipad = $key ^ $ipad;
422 $k_opad = $key ^ $opad;
423 return md5($k_opad . pack("H*", md5($k_ipad . $data)));
424 }
425 }
426
427 /**
428 * Check the gateway MD5 response to make sure that this is a proper
429 * gateway response
430 *
431 * @param string $responseMD5 MD5 hash generated by the gateway
432 * @param string $transaction_id Transaction id generated by the gateway
433 * @param string $amount Purchase amount
434 *
435 * @return bool
436 */
437 function checkMD5($responseMD5, $transaction_id, $amount, $ipn = FALSE) {
438 // cannot check if no MD5 hash
439 $md5Hash = $this->_getParam('md5Hash');
440 if (empty($md5Hash)) {
441 return TRUE;
442 }
443 $loginid = $this->_getParam('apiLogin');
444 $hashString = $ipn ? ($md5Hash . $transaction_id . $amount) : ($md5Hash . $loginid . $transaction_id . $amount);
445 $result = strtoupper(md5($hashString));
446
447 if ($result == $responseMD5) {
448 return TRUE;
449 }
450 else {
451 return FALSE;
452 }
453 }
454
455 /**
456 * Calculate and return the transaction fingerprint
457 *
458 * @return string fingerprint
459 **/
460 function CalculateFP() {
461 $x_tran_key = $this->_getParam('paymentKey');
462 $loginid = $this->_getParam('apiLogin');
463 $sequence = $this->_getParam('sequence');
464 $timestamp = $this->_getParam('timestamp');
465 $amount = $this->_getParam('amount');
466 $currency = $this->_getParam('currencyID');
467 $transaction = "$loginid^$sequence^$timestamp^$amount^$currency";
468 return $this->hmac($x_tran_key, $transaction);
469 }
470
471 /**
472 * Split a CSV file. Requires , as delimiter and " as enclosure.
473 * Based off notes from http://php.net/fgetcsv
474 *
475 * @param string $data a single CSV line
476 *
477 * @return array CSV fields
478 */
479 function explode_csv($data) {
480 $data = trim($data);
481 //make it easier to parse fields with quotes in them
482 $data = str_replace('""', "''", $data);
483 $fields = array();
484
485 while ($data != '') {
486 $matches = array();
487 if ($data[0] == '"') {
488 // handle quoted fields
489 preg_match('/^"(([^"]|\\")*?)",?(.*)$/', $data, $matches);
490
491 $fields[] = str_replace("''", '"', $matches[1]);
492 $data = $matches[3];
493 }
494 else {
495 preg_match('/^([^,]*),?(.*)$/', $data, $matches);
496
497 $fields[] = $matches[1];
498 $data = $matches[2];
499 }
500 }
501 return $fields;
502 }
503
504 /**
505 * Extract variables from returned XML
506 *
507 * Function is from Authorize.Net sample code, and used
508 * to prevent the requirement of XML functions.
509 *
510 * @param string $content XML reply from Authorize.Net
511 *
512 * @return array refId, resultCode, code, text, subscriptionId
513 */
514 function _parseArbReturn($content) {
515 $refId = $this->_substring_between($content, '<refId>', '</refId>');
516 $resultCode = $this->_substring_between($content, '<resultCode>', '</resultCode>');
517 $code = $this->_substring_between($content, '<code>', '</code>');
518 $text = $this->_substring_between($content, '<text>', '</text>');
519 $subscriptionId = $this->_substring_between($content, '<subscriptionId>', '</subscriptionId>');
520 return array(
521 'refId' => $refId,
522 'resultCode' => $resultCode,
523 'code' => $code,
524 'text' => $text,
525 'subscriptionId' => $subscriptionId,
526 );
527 }
528
529 /**
530 * Helper function for _parseArbReturn
531 *
532 * Function is from Authorize.Net sample code, and used to avoid using
533 * PHP5 XML functions
534 */
535 function _substring_between(&$haystack, $start, $end) {
536 if (strpos($haystack, $start) === FALSE || strpos($haystack, $end) === FALSE) {
537 return FALSE;
538 }
539 else {
540 $start_position = strpos($haystack, $start) + strlen($start);
541 $end_position = strpos($haystack, $end);
542 return substr($haystack, $start_position, $end_position - $start_position);
543 }
544 }
545
546 /**
547 * Get the value of a field if set
548 *
549 * @param string $field the field
550 *
551 * @return mixed value of the field, or empty string if the field is
552 * not set
553 */
554 function _getParam($field, $xmlSafe = FALSE) {
555 $value = CRM_Utils_Array::value($field, $this->_params, '');
a2dd09cc 556 if ($xmlSafe) {
6a488035 557 $value = str_replace(array( '&', '"', "'", '<', '>' ), '', $value);
a2dd09cc 558 }
6a488035
TO
559 return $value;
560 }
561
562 function &error($errorCode = NULL, $errorMessage = NULL) {
563 $e = CRM_Core_Error::singleton();
564 if ($errorCode) {
565 $e->push($errorCode, 0, array( ), $errorMessage);
566 }
567 else {
568 $e->push(9001, 0, array( ), 'Unknown System Error.');
569 }
570 return $e;
571 }
572
573 /**
574 * Set a field to the specified value. Value must be a scalar (int,
575 * float, string, or boolean)
576 *
577 * @param string $field
578 * @param mixed $value
579 *
580 * @return bool false if value is not a scalar, true if successful
581 */
582 function _setParam($field, $value) {
583 if (!is_scalar($value)) {
584 return FALSE;
585 }
586 else {
587 $this->_params[$field] = $value;
588 }
589 }
590
591 /**
592 * This function checks to see if we have the right config values
593 *
594 * @return string the error message if any
595 * @public
596 */
597 function checkConfig() {
598 $error = array();
599 if (empty($this->_paymentProcessor['user_name'])) {
600 $error[] = ts('APILogin is not set for this payment processor');
601 }
602
603 if (empty($this->_paymentProcessor['password'])) {
604 $error[] = ts('Key is not set for this payment processor');
605 }
606
607 if (!empty($error)) {
608 return implode('<p>', $error);
609 }
610 else {
611 return NULL;
612 }
613 }
614
615 function accountLoginURL() {
616 return ($this->_mode == 'test') ? 'https://test.authorize.net' : 'https://authorize.net';
617 }
618
619 function cancelSubscription(&$message = '', $params = array(
620 )) {
621 $template = CRM_Core_Smarty::singleton();
622
623 $template->assign('subscriptionType', 'cancel');
624
625 $template->assign('apiLogin', $this->_getParam('apiLogin'));
626 $template->assign('paymentKey', $this->_getParam('paymentKey'));
627 $template->assign('subscriptionId', CRM_Utils_Array::value('subscriptionId', $params));
628
629 $arbXML = $template->fetch('CRM/Contribute/Form/Contribution/AuthorizeNetARB.tpl');
630
631 // submit to authorize.net
632 $submit = curl_init($this->_paymentProcessor['url_recur']);
633 if (!$submit) {
634 return self::error(9002, 'Could not initiate connection to payment gateway');
635 }
636
637 curl_setopt($submit, CURLOPT_RETURNTRANSFER, 1);
638 curl_setopt($submit, CURLOPT_HTTPHEADER, array("Content-Type: text/xml"));
639 curl_setopt($submit, CURLOPT_HEADER, 1);
640 curl_setopt($submit, CURLOPT_POSTFIELDS, $arbXML);
641 curl_setopt($submit, CURLOPT_POST, 1);
642 curl_setopt($submit, CURLOPT_SSL_VERIFYPEER, CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'verifySSL'));
643
644 $response = curl_exec($submit);
645
646 if (!$response) {
647 return self::error(curl_errno($submit), curl_error($submit));
648 }
649
650 curl_close($submit);
651
652 $responseFields = $this->_ParseArbReturn($response);
653 $message = "{$responseFields['code']}: {$responseFields['text']}";
654
655 if ($responseFields['resultCode'] == 'Error') {
656 return self::error($responseFields['code'], $responseFields['text']);
657 }
658 return TRUE;
659 }
660
661 function updateSubscriptionBillingInfo(&$message = '', $params = array(
662 )) {
663 $template = CRM_Core_Smarty::singleton();
664 $template->assign('subscriptionType', 'updateBilling');
665
666 $template->assign('apiLogin', $this->_getParam('apiLogin'));
667 $template->assign('paymentKey', $this->_getParam('paymentKey'));
668 $template->assign('subscriptionId', $params['subscriptionId']);
669
670 $template->assign('cardNumber', $params['credit_card_number']);
671 $exp_month = str_pad($params['month'], 2, '0', STR_PAD_LEFT);
672 $exp_year = $params['year'];
673 $template->assign('expirationDate', $exp_year . '-' . $exp_month);
674
675 $template->assign('billingFirstName', $params['first_name']);
676 $template->assign('billingLastName', $params['last_name']);
677 $template->assign('billingAddress', $params['street_address']);
678 $template->assign('billingCity', $params['city']);
679 $template->assign('billingState', $params['state_province']);
680 $template->assign('billingZip', $params['postal_code']);
681 $template->assign('billingCountry', $params['country']);
682
683 $arbXML = $template->fetch('CRM/Contribute/Form/Contribution/AuthorizeNetARB.tpl');
684
685 // submit to authorize.net
686 $submit = curl_init($this->_paymentProcessor['url_recur']);
687 if (!$submit) {
688 return self::error(9002, 'Could not initiate connection to payment gateway');
689 }
690
691 curl_setopt($submit, CURLOPT_RETURNTRANSFER, 1);
692 curl_setopt($submit, CURLOPT_HTTPHEADER, array("Content-Type: text/xml"));
693 curl_setopt($submit, CURLOPT_HEADER, 1);
694 curl_setopt($submit, CURLOPT_POSTFIELDS, $arbXML);
695 curl_setopt($submit, CURLOPT_POST, 1);
696 curl_setopt($submit, CURLOPT_SSL_VERIFYPEER, CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'verifySSL'));
697
698 $response = curl_exec($submit);
699
700 if (!$response) {
701 return self::error(curl_errno($submit), curl_error($submit));
702 }
703
704 curl_close($submit);
705
706 $responseFields = $this->_ParseArbReturn($response);
707 $message = "{$responseFields['code']}: {$responseFields['text']}";
708
709 if ($responseFields['resultCode'] == 'Error') {
710 return self::error($responseFields['code'], $responseFields['text']);
711 }
712 return TRUE;
713 }
714
715 function changeSubscriptionAmount(&$message = '', $params = array(
716 )) {
717 $template = CRM_Core_Smarty::singleton();
718
719 $template->assign('subscriptionType', 'update');
720
721 $template->assign('apiLogin', $this->_getParam('apiLogin'));
722 $template->assign('paymentKey', $this->_getParam('paymentKey'));
723
724 $template->assign('subscriptionId', $params['subscriptionId']);
725 $template->assign('totalOccurrences', $params['installments']);
726 $template->assign('amount', $params['amount']);
727
728 $arbXML = $template->fetch('CRM/Contribute/Form/Contribution/AuthorizeNetARB.tpl');
729
730 // submit to authorize.net
731 $submit = curl_init($this->_paymentProcessor['url_recur']);
732 if (!$submit) {
733 return self::error(9002, 'Could not initiate connection to payment gateway');
734 }
735
736 curl_setopt($submit, CURLOPT_RETURNTRANSFER, 1);
737 curl_setopt($submit, CURLOPT_HTTPHEADER, array("Content-Type: text/xml"));
738 curl_setopt($submit, CURLOPT_HEADER, 1);
739 curl_setopt($submit, CURLOPT_POSTFIELDS, $arbXML);
740 curl_setopt($submit, CURLOPT_POST, 1);
741 curl_setopt($submit, CURLOPT_SSL_VERIFYPEER, CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'verifySSL'));
742
743 $response = curl_exec($submit);
744
745 if (!$response) {
746 return self::error(curl_errno($submit), curl_error($submit));
747 }
748
749 curl_close($submit);
750
751 $responseFields = $this->_ParseArbReturn($response);
752 $message = "{$responseFields['code']}: {$responseFields['text']}";
753
754 if ($responseFields['resultCode'] == 'Error') {
755 return self::error($responseFields['code'], $responseFields['text']);
756 }
757 return TRUE;
758 }
759}
760