PageRenderTime 26ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/payment_drivers/quickbooksms_driver.php

http://github.com/calvinfroedge/PHP-Payments
PHP | 404 lines | 304 code | 48 blank | 52 comment | 16 complexity | 0d33e786c379f90a33772d868f6243f9 MD5 | raw file
  1. <?php
  2. class QuickBooksMS_Driver extends Payment_Driver
  3. {
  4. /**
  5. * The PHP-Payments method
  6. */
  7. private $_lib_method;
  8. /**
  9. * The API method currently being utilized
  10. */
  11. private $_api_method;
  12. /**
  13. * The API method currently being utilized
  14. */
  15. private $_api_endpoint;
  16. /**
  17. * The endpoint to use for test requests
  18. */
  19. private $_api_endpoint_test = 'https://merchantaccount.ptc.quickbooks.com/j/AppGateway';
  20. /**
  21. * The endpoint to use for production requests
  22. */
  23. private $_api_endpoint_production = 'https://merchantaccount.quickbooks.com/j/AppGateway';
  24. /**
  25. * Version
  26. */
  27. private $_version = '4.5';
  28. /**
  29. * An array for storing all settings
  30. */
  31. private $_settings = array();
  32. /**
  33. * An array for storing all request data
  34. */
  35. private $_request = array();
  36. /**
  37. * Constructor method
  38. */
  39. public function __construct($config)
  40. {
  41. $this->_api_endpoint = ($config['mode'] == 'test') ? $this->_api_endpoint_test : $this->_api_endpoint_production;
  42. $this->_api_settings = array(
  43. 'login' => $config['api_application_login'],
  44. 'connection_ticket' => $config['api_connection_ticket'],
  45. 'xml_version' => '1.0',
  46. 'encoding' => 'utf-8',
  47. 'xml_extra' => 'qbmsxml version="'.$this->_version.'"'
  48. );
  49. }
  50. /**
  51. * Call Magic Method
  52. */
  53. public function __call($method, $params)
  54. {
  55. $this->_lib_method = $method;
  56. $args = $params[0];
  57. try {
  58. $request = $this->_build_request($args);
  59. }
  60. catch(Exception $e){
  61. return Payment_Response::instance()->gateway_response(
  62. 'failure',
  63. $e->getMessage(),
  64. ''
  65. );
  66. }
  67. $response_raw = Payment_Request::curl_request($this->_api_endpoint, $request, "application/x-qbmsxml");
  68. return $this->_parse_response($response_raw);
  69. }
  70. /**
  71. * The Method Map
  72. */
  73. public function method_map()
  74. {
  75. $map = array(
  76. 'oneoff_payment' => array(
  77. 'api' => 'CustomerCreditCardChargeRq',
  78. 'required' => array(
  79. 'cc_number',
  80. 'cc_exp',
  81. 'amt'
  82. ),
  83. 'keymatch' => array(
  84. 'amt' => 'Amount',
  85. 'cc_number' => 'CreditCardNumber',
  86. 'cc_exp' => 'ExpirationMonth,ExpirationYear',
  87. 'first_name' => 'NameOnCard',
  88. 'last_name' => 'NameOnCard',
  89. 'street' => 'CreditCardAddress',
  90. 'postal_code' => 'CreditCardPostalCode',
  91. 'tax_amt' => 'SalesTaxAmt',
  92. 'cc_code' => 'CardSecurityCode'
  93. )
  94. ),
  95. 'authorize_payment' => array(
  96. 'api' => 'CustomerCreditCardAuthRq',
  97. 'required' => array(
  98. 'cc_number',
  99. 'cc_exp',
  100. 'amt'
  101. ),
  102. 'keymatch' => array(
  103. 'amt' => 'Amount',
  104. 'cc_number' => 'CreditCardNumber',
  105. 'cc_exp' => 'ExpirationMonth,ExpirationYear',
  106. 'first_name' => 'NameOnCard',
  107. 'last_name' => 'NameOnCard',
  108. 'street' => 'CreditCardAddress',
  109. 'postal_code' => 'CreditCardPostalCode',
  110. 'tax_amt' => 'SalesTaxAmt',
  111. 'cc_code' => 'CardSecurityCode'
  112. ),
  113. 'static' => array(
  114. 'isCardPresent' => '0'
  115. )
  116. ),
  117. 'capture_payment' => array(
  118. 'api' => 'CustomerCreditCardCaptureRq',
  119. 'required' => array(
  120. 'identifier'
  121. ),
  122. 'keymatch' => array(
  123. 'identifier' => 'CreditCardTransID'
  124. )
  125. ),
  126. 'void_payment' => array(
  127. 'api' => 'CustomerCreditCardTxnVoidRq',
  128. 'required' => array(
  129. 'identifier'
  130. ),
  131. 'keymatch' => array(
  132. 'identifier' => 'CreditCardTransID'
  133. )
  134. ),
  135. 'refund_payment' => array(
  136. 'api' => 'CustomerCreditCardTxnVoidOrRefundRq',
  137. 'required' => array(
  138. 'identifier',
  139. 'amt'
  140. ),
  141. 'keymatch' => array(
  142. 'identifier' => 'CreditCardTransID',
  143. 'amt' => 'Amount',
  144. )
  145. ),
  146. 'recurring_payment' => array(
  147. 'api' => 'CustomerCreditCardChargeRq',
  148. 'required' => array(
  149. 'cc_number',
  150. 'cc_exp',
  151. 'amt'
  152. ),
  153. 'keymatch' => array(
  154. 'amt' => 'Amount',
  155. 'cc_number' => 'CreditCardNumber',
  156. 'cc_exp' => 'ExpirationMonth,ExpirationYear',
  157. 'first_name' => 'NameOnCard',
  158. 'last_name' => 'NameOnCard',
  159. 'street' => 'CreditCardAddress',
  160. 'postal_code' => 'CreditCardPostalCode',
  161. 'tax_amt' => 'SalesTaxAmt',
  162. 'cc_code' => 'CardSecurityCode'
  163. ),
  164. 'static' => array(
  165. 'isRecurring' => '1'
  166. )
  167. )
  168. );
  169. return $map;
  170. }
  171. /**
  172. * Builds a request
  173. * @param array array of params
  174. * @param string the api call to use
  175. * @param string the type of transaction
  176. * @return array Array of transaction settings
  177. */
  178. protected function _build_request($params)
  179. {
  180. $session = $this->_get_session_ticket();
  181. $map = $this->method_map();
  182. $l = $this->_lib_method;
  183. $nodes = array();
  184. $nodes['SignonMsgsRq'] = array(
  185. 'SignonTicketRq' => array(
  186. 'ClientDateTime' => $session->time,
  187. 'SessionTicket' => $session->ticket
  188. )
  189. );
  190. $unordered = array();
  191. $unordered['TransRequestID'] = mt_rand(1, 1000000); //This is used to avoid duplicate transactions coming from the merchant.
  192. foreach($params as $k=>$v)
  193. {
  194. if(isset($map[$l]['keymatch'][$k]))
  195. {
  196. $key = $map[$l]['keymatch'][$k];
  197. if(strpos($key, ',') !== false)
  198. {
  199. $ex = explode(',', $key);
  200. if($k == 'cc_exp')
  201. {
  202. $month = substr($params['cc_exp'], 0, 2);
  203. $year = substr($params['cc_exp'], -4, 4);
  204. $unordered[$ex[0]] = $month;
  205. $unordered[$ex[1]] = $year;
  206. }
  207. }
  208. else
  209. {
  210. if(isset($root[$key]))
  211. {
  212. $unordered[$key] .= $v;
  213. }
  214. else
  215. {
  216. $unordered[$key] = $v;
  217. }
  218. }
  219. }
  220. else
  221. {
  222. error_log("$k is not a valid param for the $l method of QuickBooksMS");
  223. }
  224. }
  225. if(isset($map[$l]['static']))
  226. {
  227. $static = $map[$l]['static'];
  228. foreach($static as $k=>$v)
  229. {
  230. $unordered[$k] = $v;
  231. }
  232. }
  233. //QuickBooks requires a specific sort order...
  234. $order = array('TransRequestID', 'CreditCardNumber', 'ExpirationMonth', 'ExpirationYear', 'isCardPresent', 'isRecurring', 'Amount', 'NameOnCard', 'CreditCardAddress', 'CreditCardCity', 'CreditCardState', 'CreditCardPostalCode', 'CommercialCode', 'SalesTaxAmount', 'CardSecurityCode', 'Lodging', 'ClientTransID', 'InvoiceID', 'Comment');
  235. $ordered = Payment_Utility::sort_array_by_array($unordered, $order);
  236. $nodes['QBMSXMLMsgsRq'] = array(
  237. $map[$l]['api'] => $ordered
  238. );
  239. $request = Payment_Request::build_xml_request(
  240. $this->_api_settings['xml_version'],
  241. $this->_api_settings['encoding'],
  242. $nodes,
  243. 'QBMSXML',
  244. null,
  245. $this->_api_settings['xml_extra']
  246. );
  247. return $request;
  248. }
  249. /**
  250. * Get the Session Ticket So We Can Create Transactions
  251. *
  252. * @return object $session->time, $session->ticket
  253. */
  254. private function _get_session_ticket()
  255. {
  256. $nodes = array();
  257. $nodes['SignonMsgsRq'] = array(
  258. 'SignonDesktopRq' => array(
  259. 'ClientDateTime' => gmdate('c'),
  260. 'ApplicationLogin' => $this->_api_settings['login'],
  261. 'ConnectionTicket' => $this->_api_settings['connection_ticket']
  262. )
  263. );
  264. $request = Payment_Request::build_xml_request(
  265. $this->_api_settings['xml_version'],
  266. $this->_api_settings['encoding'],
  267. $nodes,
  268. 'QBMSXML',
  269. null,
  270. $this->_api_settings['xml_extra']
  271. );
  272. $response_raw = Payment_Request::curl_request($this->_api_endpoint, $request, "application/x-qbmsxml");
  273. if(isset($response_raw->SignonMsgsRs->SignonDesktopRs))
  274. {
  275. $r = $response_raw->SignonMsgsRs->SignonDesktopRs;
  276. $session = (object) array(
  277. 'time' => $r->ServerDateTime,
  278. 'ticket' => $r->SessionTicket
  279. );
  280. return $session;
  281. }
  282. else
  283. {
  284. throw new Exception('authentication_failure');
  285. }
  286. }
  287. /**
  288. * Parse the response from the server
  289. *
  290. * @param array
  291. * @return object
  292. */
  293. protected function _parse_response($xml)
  294. {
  295. $details = (object) array();
  296. $as_array = Payment_Utility::arrayize_object($xml);
  297. $signon = (isset($as_array['SignonMsgsRs'])) ? $as_array['SignonMsgsRs'] : '';
  298. $response = (isset($as_array['QBMSXMLMsgsRs'])) ? $as_array['QBMSXMLMsgsRs'] : '';
  299. $result = '';
  300. $message = '';
  301. $identifier = '';
  302. if(isset($response['CustomerCreditCardChargeRs']))
  303. {
  304. $result = $response['CustomerCreditCardChargeRs']['@attributes']['statusCode'];
  305. $message = $response['CustomerCreditCardChargeRs']['@attributes']['statusMessage'];
  306. $identifier = $response['CustomerCreditCardChargeRs']['CreditCardTransID'];
  307. }
  308. if(isset($response['CustomerCreditCardAuthRs']))
  309. {
  310. $result = $response['CustomerCreditCardAuthRs']['@attributes']['statusCode'];
  311. $message = $response['CustomerCreditCardAuthRs']['@attributes']['statusMessage'];
  312. $identifier = $response['CustomerCreditCardAuthRs']['CreditCardTransID'];
  313. }
  314. if(isset($response['CustomerCreditCardCaptureRs']))
  315. {
  316. $result = $response['CustomerCreditCardCaptureRs']['@attributes']['statusCode'];
  317. $message = $response['CustomerCreditCardCaptureRs']['@attributes']['statusMessage'];
  318. $identifier = $response['CustomerCreditCardCaptureRs']['CreditCardTransID'];
  319. }
  320. if(isset($response['CustomerCreditCardTxnVoidRs']))
  321. {
  322. $result = $response['CustomerCreditCardTxnVoidRs']['@attributes']['statusCode'];
  323. $message = $response['CustomerCreditCardTxnVoidRs']['@attributes']['statusMessage'];
  324. $identifier = $response['CustomerCreditCardTxnVoidRs']['CreditCardTransID'];
  325. }
  326. if(isset($response['CustomerCreditCardTxnVoidOrRefundRs']))
  327. {
  328. $result = $response['CustomerCreditCardTxnVoidOrRefundRs']['@attributes']['statusCode'];
  329. $message = $response['CustomerCreditCardTxnVoidOrRefundRs']['@attributes']['statusMessage'];
  330. if(isset($response['CustomerCreditCardTxnVoidOrRefundRs']['CreditCardTransID']))
  331. {
  332. $identifier = $response['CustomerCreditCardTxnVoidOrRefundRs']['CreditCardTransID'];
  333. }
  334. }
  335. $details->gateway_response = $as_array;
  336. if($result === '0')
  337. { //Transaction was successful
  338. $details->identifier = $identifier;
  339. $details->timestamp = (isset($signon['ServerDateTime'])) ? $signon['ServerDateTime'] : '';
  340. return Payment_Response::instance()->gateway_response(
  341. 'Success',
  342. $this->_lib_method.'_success',
  343. $details
  344. );
  345. }
  346. else
  347. { //Transaction failed
  348. $details->reason = $message;
  349. return Payment_Response::instance()->gateway_response(
  350. 'Failure',
  351. $this->_lib_method.'_gateway_failure',
  352. $details
  353. );
  354. }
  355. }
  356. }