A Qcodo based CMS/ecommerce framework
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

419 lines
19 KiB

13 years ago
  1. <?php
  2. /**
  3. * This file is a part of Quasi CMS
  4. *@package Quasi
  5. */
  6. if(!defined('QUASICMS') ) die('No Quasi.');
  7. if (!defined("AUTHORIZENETAIMACTION.CLASS.PHP")){
  8. define("AUTHORIZENETAIMACTION.CLASS.PHP",1);
  9. /**
  10. * Class AuthorizeNetAIMAction - Authorize.net AIM payment action
  11. *
  12. * This class provides an interface to the Authorize.net AIM API. It sends credit card and
  13. * order information (via SSL) to the API server and handles the response. It will set
  14. * Approved = true on success and store any message returned. If the transaction is
  15. * not approved the order will be deleted and error messages are available for display.
  16. * Additionally, the error code and error reason code will be stored. If the transaction is
  17. * approved the order status will be updated, order_status_history and order_totals will
  18. * be inserted and a confirmation email sent to the customer in the base class.
  19. *
  20. *@todo
  21. * - deal with "Transaction Id"? Might log them like PayPal ..
  22. * - handle errors better, messages - eg. what failed ..
  23. * - implement address verification check - ie. add ship_to_* fields and handle response
  24. * - add optional customer email notification (from authorize)?
  25. * - add "Send Shipping address .." option?
  26. *
  27. *
  28. *@author Erik Winn <erikwinnmail@yahoo.com>
  29. *
  30. * $Id: AuthorizeNetAIMAction.class.php 458 2008-12-23 20:12:46Z erikwinn $
  31. *@version 0.1
  32. *
  33. *@copyright (C) 2008 by Erik Winn
  34. *@license GPL v.2
  35. This program is free software; you can redistribute it and/or modify
  36. it under the terms of the GNU General Public License as published by
  37. the Free Software Foundation; either version 2 of the License, or
  38. (at your option) any later version.
  39. This program is distributed in the hope that it will be useful,
  40. but WITHOUT ANY WARRANTY; without even the implied warranty of
  41. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  42. GNU General Public License for more details.
  43. You should have received a copy of the GNU General Public License
  44. along with this program; if not, write to the Free Software
  45. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
  46. *
  47. *@package Quasi
  48. * @subpackage Classes
  49. */
  50. class AuthorizeNetAIMAction extends PaymentActionBase
  51. {
  52. /**
  53. * @var array Name - Value pairs with which to construct AIM POST string
  54. */
  55. protected $aryRequestValues = array(
  56. 'x_login' => '',
  57. 'x_tran_key' => '',
  58. 'x_version' => '3.1',
  59. 'x_delim_char' => '|',
  60. 'x_delim_data' => 'TRUE',
  61. 'x_url' => 'FALSE',
  62. 'x_type' => 'AUTH_CAPTURE',
  63. 'x_method' => 'CC',
  64. 'x_relay_response' => 'FALSE',
  65. 'x_card_num' => '4242424242424242',
  66. 'x_exp_date' => '1209',
  67. 'x_card_code' => '', //CCV no.
  68. 'x_description' => 'Recycled Toner Cartridges',
  69. 'x_amount' => '12.23',
  70. 'x_first_name' => 'Charles D.',
  71. 'x_last_name' => 'Gaulle',
  72. 'x_address' => '342 N. Main Street #150',
  73. 'x_city' => 'Ft. Worth',
  74. 'x_state' => 'TX',
  75. 'x_country' => 'USA',
  76. 'x_zip' => '12345',
  77. 'x_email' => 'FALSE',
  78. 'x_email_customer' => '', //TRUE | FALSE
  79. 'x_email_header' => '',
  80. 'x_email_footer' => '',
  81. 'x_cust_id' => '',
  82. 'x_customer_ip' => '',
  83. 'x_invoice_num' => '',
  84. );
  85. /**
  86. *
  87. * @var array Response values will be stored here
  88. */
  89. protected $aryResponseValues;
  90. protected $intResponseCode ;
  91. protected $intResponseReasonCode ;
  92. protected $strResponseReasonText ;
  93. protected $strAVSResponse ;
  94. // protected $strInvoiceNumber ;
  95. protected $blnSendShippingAddress = false;
  96. /**
  97. * AuthorizeNetAIMAction Constructor
  98. * This sets various defaults specific to the Authorize.net API
  99. *
  100. * @param Order objOrder - the Order to process
  101. */
  102. public function __construct(Order $objOrder)
  103. {
  104. //Note: fixme - i don't think we will get a QCallerException here ..
  105. try {
  106. parent::__construct($objOrder);
  107. } catch (QCallerException $objExc) {
  108. $objExc->IncrementOffset();
  109. throw $objExc;
  110. }
  111. $this->blnTestMode = $objOrder->PaymentMethod->TestMode;
  112. if($this->blnTestMode)
  113. {
  114. $this->strRemoteDomainName = AUTHORIZENET_AIM_TESTURL;
  115. $this->strRemoteAccountId = AUTHORIZENET_AIM_TESTUSERNAME;
  116. $this->strTransactionKey = AUTHORIZENET_AIM_TESTTRANSACTIONKEY;
  117. }
  118. else
  119. {
  120. $this->strRemoteDomainName = AUTHORIZENET_AIM_URL;
  121. /// FIXME: put these somewhere safer and load it .. currently in config file!
  122. $this->strRemoteAccountId = AUTHORIZENET_AIM_USERNAME;
  123. $this->strTransactionKey = AUTHORIZENET_AIM_TRANSACTIONKEY;
  124. }
  125. $this->strRemoteCgiUrl = '/gateway/transact.dll';
  126. $this->strRequestType = 'POST';
  127. $this->strTemplateUri = __QUASI_CORE_TEMPLATES__ . '/AuthorizeNetAIMAction.tpl.php';
  128. }
  129. /**
  130. * Performs any preparation steps prior to submitting an actual payment.
  131. *@return bool true on success
  132. */
  133. public function PreProcess()
  134. {
  135. $this->createRequest();
  136. return ! $this->HasErrors;
  137. }
  138. /**
  139. * Performs the actual payment submission
  140. *@return bool true on success
  141. */
  142. public function Process()
  143. {
  144. $this->submitRequest();
  145. return ! $this->HasErrors;
  146. }
  147. /**
  148. * Performs any steps necessary after submitting an actual payment.
  149. * Updating order_status_history, order totals and confirmation email
  150. * is also called here on approval (these actions actually performed in
  151. * completeOrder ..)
  152. *@return bool true on success
  153. */
  154. public function PostProcess()
  155. {
  156. $this->handleResponse();
  157. if($this->blnApproved)
  158. $this->completeOrder();
  159. return ! $this->HasErrors;
  160. }
  161. /**
  162. * Parses the response from the payment service provider into an array
  163. * for convenience. It also sets the Response codes, messages, and blnApproved.
  164. * On failure or error messages will be in strErrors.
  165. */
  166. protected function handleResponse()
  167. {
  168. $objAuthNetTransaction = new AuthorizeNetTransaction();
  169. $objAuthNetTransaction->OrderId = $this->objOrder->Id;
  170. //default to an error ..
  171. $objAuthNetTransaction->ResponseCode = 3;
  172. $objAuthNetTransaction->ResponseReasonCode = 555;
  173. $objAuthNetTransaction->ResponseReasonText = 'Unknown Server Error';
  174. $this->aryResponseValues = array();
  175. $pos = strpos( $this->strResponse,'|' );
  176. if(false === $pos)
  177. {
  178. $this->intResponseCode = 3;
  179. $this->intResponseReasonCode = 555;
  180. }
  181. else
  182. {
  183. //remove everything except the integer at the end just before the pipe (the "ResponseCode")..
  184. $strTemp = substr($this->strResponse, $pos - 1);
  185. $this->aryResponseValues = explode('|', $strTemp );
  186. $objAuthNetTransaction->TransactionId = $this->aryResponseValues[ AuthorizeNetTransaction::TransactionIdIdx ];
  187. $objAuthNetTransaction->TransactionType = $this->aryResponseValues[ AuthorizeNetTransaction::TransactionTypeIdx ];
  188. $objAuthNetTransaction->ResponseCode = $this->aryResponseValues[ AuthorizeNetTransaction::ResponseCodeIdx ];
  189. $objAuthNetTransaction->ResponseSubcode = $this->aryResponseValues[ AuthorizeNetTransaction::ResponseSubcodeIdx ];
  190. $objAuthNetTransaction->ResponseReasonCode = $this->aryResponseValues[ AuthorizeNetTransaction::ResponseReasonCodeIdx ];
  191. $objAuthNetTransaction->ResponseReasonText = $this->aryResponseValues[ AuthorizeNetTransaction::ResponseReasonTextIdx ];
  192. $objAuthNetTransaction->AuthorizationCode = $this->aryResponseValues[ AuthorizeNetTransaction::AuthorizationCodeIdx ];
  193. $objAuthNetTransaction->AvsResponseCode = $this->aryResponseValues[ AuthorizeNetTransaction::AVSResponseIdx ];
  194. $objAuthNetTransaction->CcvResponseCode = $this->aryResponseValues[ AuthorizeNetTransaction::CCVResponseIdx ];
  195. $objAuthNetTransaction->CavResponseCode = $this->aryResponseValues[ AuthorizeNetTransaction::CAVResponseIdx ];
  196. $objAuthNetTransaction->Amount = $this->aryResponseValues[ AuthorizeNetTransaction::AmountIdx ];
  197. $this->intResponseCode = $objAuthNetTransaction->ResponseCode;
  198. $this->intResponseReasonCode = $objAuthNetTransaction->ResponseReasonCode;
  199. $this->strResponseReasonText = $objAuthNetTransaction->ResponseReasonText;
  200. $this->strAVSResponse = $objAuthNetTransaction->AvsResponseCode;
  201. }
  202. $objAuthNetTransaction->Save();
  203. switch($this->intResponseCode)
  204. {
  205. case '1': //Approved
  206. $this->blnApproved = true;
  207. $this->strStatusText = $this->strResponseReasonText;
  208. $this->blnHasErrors = false;
  209. break;
  210. case '2': //Declined
  211. $this->blnApproved = false;
  212. $this->strStatusText = $this->strResponseReasonText;
  213. $this->blnHasErrors = false;
  214. $this->objOrder->Delete();
  215. break;
  216. case '3': //Error
  217. $this->strErrors = $this->strResponseReasonText;
  218. /* . '<br />Response code: ' . $this->intResponseCode
  219. .'<br />Response: ' . $this->strResponse;*/
  220. switch($this->intResponseReasonCode)
  221. {
  222. case '103':
  223. $this->strErrors .= '<br />Valid Fingerprint, Transaction Key or Password Required. ';
  224. break;
  225. case '555':
  226. $this->strErrors .= '<br /> Invalid server response. ';
  227. break;
  228. default:
  229. $this->strErrors .= '<br />Unknown internal error. ';
  230. }
  231. $this->blnApproved = false;
  232. $this->blnHasErrors = true;
  233. $this->objOrder->Delete();
  234. break;
  235. case '4': //Held for review
  236. ///@todo handle held for review payments (authnet)
  237. $this->blnApproved = false;
  238. $this->blnHasErrors = false;
  239. break;
  240. default:
  241. $this->blnApproved = false;
  242. $this->blnHasErrors = true;
  243. $this->strErrors = $this->strResponseReasonText
  244. . '<br />Unknown Response code: ' . $this->intResponseCode
  245. .'<br />Response: ' . $this->strResponse;
  246. // $this->objOrder->Delete();
  247. }
  248. }
  249. /**
  250. * Creates GET query string for the transaction appropriate to the provider API, storing
  251. * the result in strGETRequest.
  252. * NOTE: Currently unused - we send a POST ..
  253. */
  254. protected function createGETRequest()
  255. {
  256. $this->initMerchantFields();
  257. $this->initTransactionFields();
  258. $this->initOrderFields();
  259. $this->initCustomerFields();
  260. foreach( $this->aryRequestValues as $strName => $strValue )
  261. if('' != $strValue )
  262. $this->strGETRequest .= $strName . '=' . urlencode($strValue) . '&';
  263. $this->strGETRequest = rtrim($this->strGETRequest,'&');
  264. if('' != $this->strGETRequest )
  265. $this->blnHasErrors = false;
  266. }
  267. protected function createPOSTRequest()
  268. {
  269. $this->initMerchantFields();
  270. $this->initTransactionFields();
  271. $this->initOrderFields();
  272. $this->initCustomerFields();
  273. foreach( $this->aryRequestValues as $strName => $strValue )
  274. if('' != $strValue )
  275. $this->strPOSTRequest .= $strName . '=' . urlencode($strValue) . '&';
  276. $this->strPOSTRequest = rtrim($this->strPOSTRequest,'&');
  277. if('' != $this->strPOSTRequest )
  278. $this->blnHasErrors = false;
  279. }
  280. protected function initMerchantFields()
  281. {
  282. $this->aryRequestValues['x_login'] = $this->strRemoteAccountId;
  283. $this->aryRequestValues['x_tran_key'] = $this->strTransactionKey;
  284. }
  285. protected function initTransactionFields($strTransactionId='')
  286. {
  287. /* unused - todo: .. not sure what to do with this yet.
  288. if('' != $strTransactionId)
  289. $this->aryRequestValues['x_trans_id'] = $strTransactionId;
  290. else
  291. $this->aryRequestValues['x_trans_id'] = $this->strTransactionId;
  292. $this->aryRequestValues['x_auth_code'] = $this->
  293. */
  294. $this->aryRequestValues['x_amount'] = $this->fltTotalPrice;
  295. $this->aryRequestValues['x_card_num'] = $this->strCCNumber;
  296. $this->aryRequestValues['x_exp_date'] = $this->strCCExpirationMonth . $this->strCCExpirationYear;
  297. //Note - this should be checked on input, this is for testing - remove conditional ..
  298. if( '' != $this->strCCVNumber )
  299. $this->aryRequestValues['x_card_code'] = $this->strCCVNumber;
  300. }
  301. protected function initOrderFields()
  302. {
  303. $this->aryRequestValues['x_invoice_num'] = $this->objOrder->Id;
  304. $this->aryRequestValues['x_description'] = DEFAULT_ORDER_DESCRIPTION;
  305. //todo: maybe itemized list here ..
  306. //foreach (orderitems) $this->aryRequestValues['x_line_item'] ...etc.
  307. }
  308. protected function initCustomerFields()
  309. {
  310. $this->aryRequestValues['x_customer_ip'] = $_SERVER['REMOTE_ADDR'];
  311. $this->aryRequestValues['x_cust_id'] = $this->objOrder->Account->Id;
  312. $this->aryRequestValues['x_email'] = $this->objOrder->Account->Person->EmailAddress;
  313. $this->initAddressFields();
  314. /* todo: optionally have authorize send a confirmation email ..
  315. $this->aryRequestValues['x_email_customer'] = '';
  316. $this->aryRequestValues['x_email_header'] = '';
  317. $this->aryRequestValues['x_email_footer'] = '';
  318. */
  319. }
  320. protected function initAddressFields()
  321. {
  322. //todo: if($this->SendShippingAddress)
  323. //We must concatenate all the name fields into first or last name ..
  324. if( '' != $this->objOrder->BillingNamePrefix )
  325. {
  326. $this->aryRequestValues['x_first_name'] = $this->objOrder->BillingNamePrefix . ' ';
  327. $this->aryRequestValues['x_first_name'] .= $this->objOrder->BillingFirstName;
  328. }
  329. else
  330. $this->aryRequestValues['x_first_name'] = $this->objOrder->BillingFirstName;
  331. if( '' != $this->objOrder->BillingMiddleName )
  332. $this->aryRequestValues['x_first_name'] .= ' ' . $this->objOrder->BillingMiddleName;
  333. $this->aryRequestValues['x_last_name'] = ' ' . $this->objOrder->BillingLastName;
  334. if( '' != $this->objOrder->BillingNameSuffix )
  335. $this->aryRequestValues['x_last_name'] .= ' ' . $this->objOrder->BillingNameSuffix;
  336. $this->aryRequestValues['x_address'] = $this->objOrder->BillingStreet1;
  337. //ALERT: There is no field for Street2, County or Suburb so we put them all in address .. this may
  338. // cause problems ..
  339. if( '' != $this->objOrder->BillingStreet2 )
  340. $this->aryRequestValues['x_address'] .= ', ' . $this->objOrder->BillingStreet2;
  341. if( '' != $this->objOrder->BillingSuburb )
  342. $this->aryRequestValues['x_address'] .= ', ' . $this->objOrder->BillingSuburb;
  343. if( '' != $this->objOrder->BillingCounty )
  344. $this->aryRequestValues['x_address'] .= ', ' . $this->objOrder->BillingCounty;
  345. $this->aryRequestValues['x_city'] = $this->objOrder->BillingCity;
  346. $this->aryRequestValues['x_state'] = $this->objOrder->BillingState;
  347. $this->aryRequestValues['x_country'] = $this->objOrder->BillingCountry;
  348. $this->aryRequestValues['x_zip'] = $this->objOrder->BillingPostalCode;
  349. }
  350. public function __get($strName)
  351. {
  352. switch ($strName)
  353. {
  354. case 'SendShippingAddress':
  355. return $this->blnSendShippingAddress;
  356. default:
  357. try {
  358. return parent::__get($strName);
  359. } catch (QCallerException $objExc) {
  360. $objExc->IncrementOffset();
  361. throw $objExc;
  362. }
  363. }
  364. }
  365. public function __set($strName, $mixValue)
  366. {
  367. switch ($strName)
  368. {
  369. case 'SendShippingAddress':
  370. try {
  371. $this->blnSendShippingAddress = QType::Cast($mixValue, QType::Boolean );
  372. } catch (QInvalidCastException $objExc) {
  373. $objExc->IncrementOffset();
  374. throw $objExc;
  375. }
  376. default:
  377. try {
  378. return (parent::__set($strName, $mixValue));
  379. } catch (QCallerException $objExc) {
  380. $objExc->IncrementOffset();
  381. throw $objExc;
  382. }
  383. }
  384. }
  385. }//end class
  386. }//end define
  387. ?>