1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25:
26:
27: 28: 29: 30: 31: 32: 33:
34: class Mage_Authorizenet_Model_Directpost extends Mage_Paygate_Model_Authorizenet
35: {
36: protected $_code = 'authorizenet_directpost';
37: protected $_formBlockType = 'directpost/form';
38: protected $_infoBlockType = 'payment/info';
39:
40: 41: 42:
43: protected $_canAuthorize = true;
44: protected $_canCapture = true;
45: protected $_canCapturePartial = false;
46: protected $_canRefund = true;
47: protected $_canRefundInvoicePartial = true;
48: protected $_canVoid = true;
49: protected $_canUseInternal = true;
50: protected $_canUseCheckout = true;
51: protected $_canUseForMultishipping = false;
52: protected $_canSaveCc = false;
53: protected $_isInitializeNeeded = true;
54:
55: 56: 57: 58: 59:
60: public function validate()
61: {
62: return true;
63: }
64:
65: 66: 67: 68: 69: 70: 71: 72:
73: public function authorize(Varien_Object $payment, $amount)
74: {
75: $payment->setAdditionalInformation('payment_type', $this->getConfigData('payment_action'));
76: }
77:
78: 79: 80: 81: 82: 83: 84: 85:
86: public function capture(Varien_Object $payment, $amount)
87: {
88: if ($amount <= 0) {
89: Mage::throwException(Mage::helper('paygate')->__('Invalid amount for capture.'));
90: }
91:
92: $payment->setAmount($amount);
93:
94: if ($payment->getParentTransactionId()) {
95: $payment->setAnetTransType(self::REQUEST_TYPE_PRIOR_AUTH_CAPTURE);
96: $payment->setXTransId($this->_getRealParentTransactionId($payment));
97: } else {
98: $payment->setAnetTransType(self::REQUEST_TYPE_AUTH_CAPTURE);
99: }
100:
101: $request= $this->_buildRequest($payment);
102: $result = $this->_postRequest($request);
103:
104: switch ($result->getResponseCode()) {
105: case self::RESPONSE_CODE_APPROVED:
106: if ($result->getResponseReasonCode() == self::RESPONSE_REASON_CODE_APPROVED) {
107: if (!$payment->getParentTransactionId() ||
108: $result->getTransactionId() != $payment->getParentTransactionId()) {
109: $payment->setTransactionId($result->getTransactionId());
110: }
111: $payment
112: ->setIsTransactionClosed(0)
113: ->setTransactionAdditionalInfo($this->_realTransactionIdKey, $result->getTransactionId());
114: return $this;
115: }
116: Mage::throwException($this->_wrapGatewayError($result->getResponseReasonText()));
117: case self::RESPONSE_CODE_DECLINED:
118: case self::RESPONSE_CODE_ERROR:
119: Mage::throwException($this->_wrapGatewayError($result->getResponseReasonText()));
120: default:
121: Mage::throwException(Mage::helper('paygate')->__('Payment capturing error.'));
122: }
123: }
124:
125: 126: 127: 128: 129:
130: public function canRefund()
131: {
132: return $this->_canRefund;
133: }
134:
135: 136: 137: 138: 139: 140:
141: public function canVoid(Varien_Object $payment)
142: {
143: return $this->_canVoid;
144: }
145:
146: 147: 148: 149: 150: 151: 152:
153: public function void(Varien_Object $payment)
154: {
155: if (!$payment->getParentTransactionId()) {
156: Mage::throwException(Mage::helper('paygate')->__('Invalid transaction ID.'));
157: }
158:
159: $payment->setAnetTransType(self::REQUEST_TYPE_VOID);
160: $payment->setXTransId($this->_getRealParentTransactionId($payment));
161:
162: $request = $this->_buildRequest($payment);
163: $result = $this->_postRequest($request);
164:
165: switch ($result->getResponseCode()) {
166: case self::RESPONSE_CODE_APPROVED:
167: if ($result->getResponseReasonCode() == self::RESPONSE_REASON_CODE_APPROVED) {
168: if ($result->getTransactionId() != $payment->getParentTransactionId()) {
169: $payment->setTransactionId($result->getTransactionId());
170: }
171: $payment
172: ->setIsTransactionClosed(1)
173: ->setShouldCloseParentTransaction(1)
174: ->setTransactionAdditionalInfo($this->_realTransactionIdKey, $result->getTransactionId());
175: return $this;
176: }
177: Mage::throwException($this->_wrapGatewayError($result->getResponseReasonText()));
178: case self::RESPONSE_CODE_DECLINED:
179: case self::RESPONSE_CODE_ERROR:
180: Mage::throwException($this->_wrapGatewayError($result->getResponseReasonText()));
181: default:
182: Mage::throwException(Mage::helper('paygate')->__('Payment voiding error.'));
183: }
184: }
185:
186: 187: 188: 189: 190: 191:
192: public function processInvoice($invoice, $payment)
193: {
194: return Mage_Payment_Model_Method_Abstract::processInvoice($invoice, $payment);
195: }
196:
197: 198: 199: 200: 201: 202:
203: public function processCreditmemo($creditmemo, $payment)
204: {
205: return Mage_Payment_Model_Method_Abstract::processCreditmemo($creditmemo, $payment);
206: }
207:
208: 209: 210: 211: 212: 213: 214: 215: 216:
217: public function refund(Varien_Object $payment, $amount)
218: {
219: $last4 = $payment->getCcLast4();
220: $payment->setCcLast4($payment->decrypt($last4));
221: try {
222: $this->_refund($payment, $amount);
223: } catch (Exception $e) {
224: $payment->setCcLast4($last4);
225: throw $e;
226: }
227: $payment->setCcLast4($last4);
228: return $this;
229: }
230:
231: 232: 233: 234: 235: 236: 237:
238: protected function _refund(Varien_Object $payment, $amount)
239: {
240: if ($amount <= 0) {
241: Mage::throwException(Mage::helper('paygate')->__('Invalid amount for refund.'));
242: }
243:
244: if (!$payment->getParentTransactionId()) {
245: Mage::throwException(Mage::helper('paygate')->__('Invalid transaction ID.'));
246: }
247:
248: $payment->setAnetTransType(self::REQUEST_TYPE_CREDIT);
249: $payment->setAmount($amount);
250: $payment->setXTransId($this->_getRealParentTransactionId($payment));
251:
252: $request = $this->_buildRequest($payment);
253: $result = $this->_postRequest($request);
254:
255: switch ($result->getResponseCode()) {
256: case self::RESPONSE_CODE_APPROVED:
257: if ($result->getResponseReasonCode() == self::RESPONSE_REASON_CODE_APPROVED) {
258: if ($result->getTransactionId() != $payment->getParentTransactionId()) {
259: $payment->setTransactionId($result->getTransactionId());
260: }
261: $shouldCloseCaptureTransaction = $payment->getOrder()->canCreditmemo() ? 0 : 1;
262: $payment
263: ->setIsTransactionClosed(1)
264: ->setShouldCloseParentTransaction($shouldCloseCaptureTransaction)
265: ->setTransactionAdditionalInfo($this->_realTransactionIdKey, $result->getTransactionId());
266: return $this;
267: }
268: Mage::throwException($this->_wrapGatewayError($result->getResponseReasonText()));
269: case self::RESPONSE_CODE_DECLINED:
270: case self::RESPONSE_CODE_ERROR:
271: Mage::throwException($this->_wrapGatewayError($result->getResponseReasonText()));
272: default:
273: Mage::throwException(Mage::helper('paygate')->__('Payment refunding error.'));
274: }
275: }
276:
277: 278: 279: 280: 281:
282: public function getCgiUrl()
283: {
284: $uri = $this->getConfigData('cgi_url');
285: return $uri ? $uri : self::CGI_URL;
286: }
287:
288: 289: 290: 291: 292: 293:
294: public function getRelayUrl($storeId = null)
295: {
296: if ($storeId == null && $this->getStore()) {
297: $storeId = $this->getStore();
298: }
299: return Mage::app()->getStore($storeId)
300: ->getBaseUrl(Mage_Core_Model_Store::URL_TYPE_LINK).
301: 'authorizenet/directpost_payment/response';
302: }
303:
304: 305: 306: 307: 308:
309: protected function _getRequestModel()
310: {
311: return Mage::getModel('authorizenet/directpost_request');
312: }
313:
314: 315: 316: 317: 318:
319: public function getResponse()
320: {
321: return Mage::getSingleton('authorizenet/directpost_response');
322: }
323:
324: 325: 326: 327: 328: 329:
330: public function initialize($paymentAction, $stateObject)
331: {
332: switch ($paymentAction) {
333: case self::ACTION_AUTHORIZE:
334: case self::ACTION_AUTHORIZE_CAPTURE:
335: $payment = $this->getInfoInstance();
336: $order = $payment->getOrder();
337: $order->setCanSendNewEmailFlag(false);
338: $payment->authorize(true, $order->getBaseTotalDue());
339: $payment->setAmountAuthorized($order->getTotalDue());
340:
341: $order->setState(Mage_Sales_Model_Order::STATE_PENDING_PAYMENT, 'pending_payment', '', false);
342:
343: $stateObject->setState(Mage_Sales_Model_Order::STATE_PENDING_PAYMENT);
344: $stateObject->setStatus('pending_payment');
345: $stateObject->setIsNotified(false);
346: break;
347: default:
348: break;
349: }
350: }
351:
352: 353: 354: 355: 356: 357:
358: public function generateRequestFromOrder(Mage_Sales_Model_Order $order)
359: {
360: $request = $this->_getRequestModel();
361: $request->setConstantData($this)
362: ->setDataFromOrder($order, $this)
363: ->signRequestData();
364:
365: $this->_debug(array('request' => $request->getData()));
366:
367: return $request;
368: }
369:
370: 371: 372: 373: 374: 375:
376: public function setResponseData(array $postData)
377: {
378: $this->getResponse()->setData($postData);
379: return $this;
380: }
381:
382: 383: 384: 385: 386: 387:
388: public function validateResponse()
389: {
390: $response = $this->getResponse();
391:
392: if (!$this->getConfigData('trans_md5') || !$this->getConfigData('login') ||
393: !$response->isValidHash($this->getConfigData('trans_md5'), $this->getConfigData('login'))
394: ) {
395: Mage::throwException(
396: Mage::helper('authorizenet')->__('Response hash validation failed. Transaction declined.')
397: );
398: }
399: return true;
400: }
401:
402: 403: 404: 405: 406: 407:
408: public function process(array $responseData)
409: {
410: $debugData = array(
411: 'response' => $responseData
412: );
413: $this->_debug($debugData);
414:
415: $this->setResponseData($responseData);
416:
417:
418:
419: $this->validateResponse();
420:
421: $response = $this->getResponse();
422:
423: $orderIncrementId = $response->getXInvoiceNum();
424: $responseText = $this->_wrapGatewayError($response->getXResponseReasonText());
425: $isError = false;
426: if ($orderIncrementId) {
427:
428: $order = Mage::getModel('sales/order')->loadByIncrementId($orderIncrementId);
429: if ($order->getId() && $order->getState() == Mage_Sales_Model_Order::STATE_PENDING_PAYMENT) {
430:
431: $this->_authOrder($order);
432: } else {
433: $isError = true;
434: }
435: } else {
436: $isError = true;
437: }
438:
439: if ($isError) {
440: Mage::throwException(
441: ($responseText && !$response->isApproved()) ?
442: $responseText :
443: Mage::helper('authorizenet')->__('Payment error. Order was not found.')
444: );
445: }
446: }
447:
448: 449: 450: 451: 452:
453: protected function _fillPaymentByResponse(Varien_Object $payment)
454: {
455: $response = $this->getResponse();
456: $payment->setTransactionId($response->getXTransId())
457: ->setParentTransactionId(null)
458: ->setIsTransactionClosed(0)
459: ->setTransactionAdditionalInfo($this->_realTransactionIdKey, $response->getXTransId());
460:
461: if ($response->getXMethod() == self::REQUEST_METHOD_CC) {
462: $payment->setCcAvsStatus($response->getXAvsCode())
463: ->setCcLast4($payment->encrypt(substr($response->getXAccountNumber(), -4)));
464: }
465: }
466:
467: 468: 469: 470: 471: 472:
473: public function checkResponseCode()
474: {
475: switch ($this->getResponse()->getXResponseCode()) {
476: case self::RESPONSE_CODE_APPROVED:
477: return true;
478: case self::RESPONSE_CODE_DECLINED:
479: case self::RESPONSE_CODE_ERROR:
480: Mage::throwException($this->_wrapGatewayError($this->getResponse()->getXResponseReasonText()));
481: default:
482: Mage::throwException(Mage::helper('authorizenet')->__('Payment authorization error.'));
483: }
484: }
485:
486: 487: 488: 489: 490: 491:
492: public function checkTransId()
493: {
494: if (!$this->getResponse()->getXTransId()) {
495: Mage::throwException(
496: Mage::helper('authorizenet')->__('Payment authorization error. Transacion id is empty.')
497: );
498: }
499: return true;
500: }
501:
502: 503: 504: 505: 506: 507:
508: protected function _matchAmount($amount)
509: {
510: return sprintf('%.2F', $amount) == sprintf('%.2F', $this->getResponse()->getXAmount());
511: }
512:
513: 514: 515: 516: 517: 518:
519: protected function _authOrder(Mage_Sales_Model_Order $order)
520: {
521: try {
522: $this->checkResponseCode();
523: $this->checkTransId();
524: } catch (Exception $e) {
525:
526: $message = $e->getMessage();
527: $this->_declineOrder($order, $message, false);
528: throw $e;
529: }
530:
531: $response = $this->getResponse();
532:
533:
534: $payment = $order->getPayment();
535: $this->_fillPaymentByResponse($payment);
536:
537: $payment->addTransaction(Mage_Sales_Model_Order_Payment_Transaction::TYPE_AUTH);
538:
539:
540: $message = Mage::helper('authorizenet')->__(
541: 'Amount of %s approved by payment gateway. Transaction ID: "%s".',
542: $order->getBaseCurrency()->formatTxt($payment->getBaseAmountAuthorized()),
543: $response->getXTransId()
544: );
545:
546: $orderState = Mage_Sales_Model_Order::STATE_PROCESSING;
547: $orderStatus = $this->getConfigData('order_status');
548: if (!$orderStatus || $order->getIsVirtual()) {
549: $orderStatus = $order->getConfig()->getStateDefaultStatus($orderState);
550: }
551:
552: $order->setState($orderState, $orderStatus ? $orderStatus : true, $message, false)
553: ->save();
554:
555:
556:
557: if (!$this->_matchAmount($payment->getBaseAmountAuthorized())) {
558: $message = Mage::helper('authorizenet')->__('Payment error. Paid amount doesn\'t match the order amount.');
559: $this->_declineOrder($order, $message, true);
560: Mage::throwException($message);
561: }
562:
563:
564: $this->_captureOrder($order);
565:
566: try {
567: if (!$response->hasOrderSendConfirmation() || $response->getOrderSendConfirmation()) {
568: $order->sendNewOrderEmail();
569: }
570:
571: Mage::getModel('sales/quote')
572: ->load($order->getQuoteId())
573: ->setIsActive(false)
574: ->save();
575: } catch (Exception $e) {}
576: }
577:
578: 579: 580: 581: 582: 583: 584:
585: protected function _declineOrder(Mage_Sales_Model_Order $order, $message = '', $voidPayment = true)
586: {
587: try {
588: $response = $this->getResponse();
589: if ($voidPayment &&
590: $response->getXTransId() &&
591: strtoupper($response->getXType()) == self::REQUEST_TYPE_AUTH_ONLY
592: ) {
593: $order->getPayment()
594: ->setTransactionId(null)
595: ->setParentTransactionId($response->getXTransId())
596: ->void();
597: }
598: $order->registerCancellation($message)
599: ->save();
600: } catch (Exception $e) {
601:
602: Mage::logException($e);
603: }
604: }
605:
606: 607: 608: 609: 610:
611: protected function _captureOrder(Mage_Sales_Model_Order $order)
612: {
613: $payment = $order->getPayment();
614: if ($payment->getAdditionalInformation('payment_type') == self::ACTION_AUTHORIZE_CAPTURE) {
615: try {
616: $payment->setTransactionId(null)
617: ->setParentTransactionId($this->getResponse()->getXTransId())
618: ->capture(null);
619:
620:
621: if ($order->getState() == Mage_Sales_Model_Order::STATE_PROCESSING) {
622: $orderStatus = $this->getConfigData('order_status');
623: if (!$orderStatus || $order->getIsVirtual()) {
624: $orderStatus = $order->getConfig()
625: ->getStateDefaultStatus(Mage_Sales_Model_Order::STATE_PROCESSING);
626: }
627: if ($orderStatus) {
628: $order->setStatus($orderStatus);
629: }
630: }
631:
632: $order->save();
633: } catch (Exception $e) {
634: Mage::logException($e);
635:
636: }
637: }
638: }
639:
640: 641: 642: 643: 644: 645:
646: protected function _getRealParentTransactionId($payment)
647: {
648: $transaction = $payment->getTransaction($payment->getParentTransactionId());
649: return $transaction->getAdditionalInformation($this->_realTransactionIdKey);
650: }
651: }
652: