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: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154:
155: class Mage_Sales_Model_Order_Payment extends Mage_Payment_Model_Info
156: {
157: 158: 159: 160: 161:
162: const REVIEW_ACTION_ACCEPT = 'accept';
163: const REVIEW_ACTION_DENY = 'deny';
164: const REVIEW_ACTION_UPDATE = 'update';
165:
166: 167: 168: 169: 170:
171: protected $_order;
172:
173: 174: 175: 176: 177:
178: protected $_billingAgreement = null;
179:
180: 181: 182: 183:
184: protected $_canVoidLookup = null;
185:
186: 187: 188: 189: 190:
191: protected $_transactionsLookup = array();
192:
193: protected $_eventPrefix = 'sales_order_payment';
194: protected $_eventObject = 'payment';
195:
196: 197: 198: 199: 200:
201: protected $_transactionAdditionalInfo = array();
202:
203: 204: 205:
206: protected function _construct()
207: {
208: $this->_init('sales/order_payment');
209: }
210:
211: 212: 213: 214: 215: 216:
217: public function setOrder(Mage_Sales_Model_Order $order)
218: {
219: $this->_order = $order;
220: return $this;
221: }
222:
223: 224: 225: 226: 227:
228: public function getOrder()
229: {
230: return $this->_order;
231: }
232:
233: 234: 235: 236: 237:
238: public function canCapture()
239: {
240: if (!$this->getMethodInstance()->canCapture()) {
241: return false;
242: }
243:
244: $authTransaction = $this->getAuthorizationTransaction();
245: if ($authTransaction && $authTransaction->getIsClosed()) {
246: $orderTransaction = $this->_lookupTransaction(null, Mage_Sales_Model_Order_Payment_Transaction::TYPE_ORDER);
247: if (!$orderTransaction) {
248: return false;
249: }
250: }
251: return true;
252: }
253:
254: public function canRefund()
255: {
256: return $this->getMethodInstance()->canRefund();
257: }
258:
259: public function canRefundPartialPerInvoice()
260: {
261: return $this->getMethodInstance()->canRefundPartialPerInvoice();
262: }
263:
264: public function canCapturePartial()
265: {
266: return $this->getMethodInstance()->canCapturePartial();
267: }
268:
269: 270: 271: 272: 273: 274:
275: public function place()
276: {
277: Mage::dispatchEvent('sales_order_payment_place_start', array('payment' => $this));
278: $order = $this->getOrder();
279:
280: $this->setAmountOrdered($order->getTotalDue());
281: $this->setBaseAmountOrdered($order->getBaseTotalDue());
282: $this->setShippingAmount($order->getShippingAmount());
283: $this->setBaseShippingAmount($order->getBaseShippingAmount());
284:
285: $methodInstance = $this->getMethodInstance();
286: $methodInstance->setStore($order->getStoreId());
287:
288: $orderState = Mage_Sales_Model_Order::STATE_NEW;
289: $orderStatus= false;
290:
291: $stateObject = new Varien_Object();
292:
293: 294: 295:
296: $methodInstance->validate();
297: $action = $methodInstance->getConfigPaymentAction();
298: if ($action) {
299: if ($methodInstance->isInitializeNeeded()) {
300: 301: 302:
303: $methodInstance->initialize($methodInstance->getConfigData('payment_action'), $stateObject);
304: } else {
305: $orderState = Mage_Sales_Model_Order::STATE_PROCESSING;
306: switch ($action) {
307: case Mage_Payment_Model_Method_Abstract::ACTION_ORDER:
308: $this->_order($order->getBaseTotalDue());
309: break;
310: case Mage_Payment_Model_Method_Abstract::ACTION_AUTHORIZE:
311: $this->_authorize(true, $order->getBaseTotalDue());
312: $this->setAmountAuthorized($order->getTotalDue());
313: break;
314: case Mage_Payment_Model_Method_Abstract::ACTION_AUTHORIZE_CAPTURE:
315: $this->setAmountAuthorized($order->getTotalDue());
316: $this->setBaseAmountAuthorized($order->getBaseTotalDue());
317: $this->capture(null);
318: break;
319: default:
320: break;
321: }
322: }
323: }
324:
325: $this->_createBillingAgreement();
326:
327: $orderIsNotified = null;
328: if ($stateObject->getState() && $stateObject->getStatus()) {
329: $orderState = $stateObject->getState();
330: $orderStatus = $stateObject->getStatus();
331: $orderIsNotified = $stateObject->getIsNotified();
332: } else {
333: $orderStatus = $methodInstance->getConfigData('order_status');
334: if (!$orderStatus) {
335: $orderStatus = $order->getConfig()->getStateDefaultStatus($orderState);
336: }
337: }
338: $isCustomerNotified = (null !== $orderIsNotified) ? $orderIsNotified : $order->getCustomerNoteNotify();
339: $message = $order->getCustomerNote();
340:
341:
342: if ($order->getState() == Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW) {
343: if ($message) {
344: $order->addStatusToHistory($order->getStatus(), $message, $isCustomerNotified);
345: }
346: }
347:
348: elseif ($order->getState() && ($orderStatus !== $order->getStatus() || $message)) {
349: $order->setState($orderState, $orderStatus, $message, $isCustomerNotified);
350: }
351:
352: elseif (($order->getState() != $orderState) || ($order->getStatus() != $orderStatus) || $message) {
353: $order->setState($orderState, $orderStatus, $message, $isCustomerNotified);
354: }
355:
356: Mage::dispatchEvent('sales_order_payment_place_end', array('payment' => $this));
357:
358: return $this;
359: }
360:
361: 362: 363: 364: 365: 366: 367: 368: 369: 370: 371:
372: public function capture($invoice)
373: {
374: if (is_null($invoice)) {
375: $invoice = $this->_invoice();
376: $this->setCreatedInvoice($invoice);
377: return $this;
378: }
379: $amountToCapture = $this->_formatAmount($invoice->getBaseGrandTotal());
380: $order = $this->getOrder();
381:
382:
383: $paidWorkaround = 0;
384: if (!$invoice->wasPayCalled()) {
385: $paidWorkaround = (float)$amountToCapture;
386: }
387: $this->_isCaptureFinal($paidWorkaround);
388:
389: $this->_generateTransactionId(
390: Mage_Sales_Model_Order_Payment_Transaction::TYPE_CAPTURE,
391: $this->getAuthorizationTransaction()
392: );
393:
394: Mage::dispatchEvent('sales_order_payment_capture', array('payment' => $this, 'invoice' => $invoice));
395:
396: 397: 398: 399:
400: if ($invoice->getTransactionId()) {
401: $this->getMethodInstance()
402: ->setStore($order->getStoreId())
403: ->fetchTransactionInfo($this, $invoice->getTransactionId());
404: }
405: $status = true;
406: if (!$invoice->getIsPaid() && !$this->getIsTransactionPending()) {
407:
408: $this->getMethodInstance()->setStore($order->getStoreId())->capture($this, $amountToCapture);
409:
410: $transaction = $this->_addTransaction(
411: Mage_Sales_Model_Order_Payment_Transaction::TYPE_CAPTURE,
412: $invoice,
413: true
414: );
415:
416: if ($this->getIsTransactionPending()) {
417: $message = Mage::helper('sales')->__('Capturing amount of %s is pending approval on gateway.', $this->_formatPrice($amountToCapture));
418: $state = Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW;
419: if ($this->getIsFraudDetected()) {
420: $status = Mage_Sales_Model_Order::STATUS_FRAUD;
421: }
422: $invoice->setIsPaid(false);
423: } else {
424: $message = Mage::helper('sales')->__('Captured amount of %s online.', $this->_formatPrice($amountToCapture));
425: $state = Mage_Sales_Model_Order::STATE_PROCESSING;
426: $invoice->setIsPaid(true);
427: $this->_updateTotals(array('base_amount_paid_online' => $amountToCapture));
428: }
429: if ($order->isNominal()) {
430: $message = $this->_prependMessage(Mage::helper('sales')->__('Nominal order registered.'));
431: } else {
432: $message = $this->_prependMessage($message);
433: $message = $this->_appendTransactionToMessage($transaction, $message);
434: }
435: $order->setState($state, $status, $message);
436: $this->getMethodInstance()->processInvoice($invoice, $this);
437: return $this;
438: }
439: Mage::throwException(
440: Mage::helper('sales')->__('The transaction "%s" cannot be captured yet.', $invoice->getTransactionId())
441: );
442: }
443:
444: 445: 446: 447: 448: 449: 450: 451: 452: 453: 454: 455:
456: public function registerCaptureNotification($amount)
457: {
458: $this->_generateTransactionId(Mage_Sales_Model_Order_Payment_Transaction::TYPE_CAPTURE,
459: $this->getAuthorizationTransaction()
460: );
461:
462: $order = $this->getOrder();
463: $amount = (float)$amount;
464: $invoice = $this->_getInvoiceForTransactionId($this->getTransactionId());
465:
466:
467: if (!$invoice) {
468: if ($this->_isCaptureFinal($amount)) {
469: $invoice = $order->prepareInvoice()->register();
470: $order->addRelatedObject($invoice);
471: $this->setCreatedInvoice($invoice);
472: } else {
473: $this->setIsFraudDetected(true);
474: $this->_updateTotals(array('base_amount_paid_online' => $amount));
475: }
476: }
477:
478: $status = true;
479: if ($this->getIsTransactionPending()) {
480: $message = Mage::helper('sales')->__('Capturing amount of %s is pending approval on gateway.', $this->_formatPrice($amount));
481: $state = Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW;
482: if ($this->getIsFraudDetected()) {
483: $message = Mage::helper('sales')->__('Order is suspended as its capture amount %s is suspected to be fraudulent.', $this->_formatPrice($amount));
484: $status = Mage_Sales_Model_Order::STATUS_FRAUD;
485: }
486: } else {
487: $message = Mage::helper('sales')->__('Registered notification about captured amount of %s.', $this->_formatPrice($amount));
488: $state = Mage_Sales_Model_Order::STATE_PROCESSING;
489: if ($this->getIsFraudDetected()) {
490: $state = Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW;
491: $message = Mage::helper('sales')->__('Order is suspended as its capture amount %s is suspected to be fraudulent.', $this->_formatPrice($amount));
492: $status = Mage_Sales_Model_Order::STATUS_FRAUD;
493: }
494:
495: if ($invoice && Mage_Sales_Model_Order_Invoice::STATE_OPEN == $invoice->getState()) {
496: $invoice->pay();
497: $this->_updateTotals(array('base_amount_paid_online' => $amount));
498: $order->addRelatedObject($invoice);
499: }
500: }
501:
502: $transaction = $this->_addTransaction(Mage_Sales_Model_Order_Payment_Transaction::TYPE_CAPTURE, $invoice, true);
503: $message = $this->_prependMessage($message);
504: $message = $this->_appendTransactionToMessage($transaction, $message);
505: $order->setState($state, $status, $message);
506: return $this;
507: }
508:
509: 510: 511: 512: 513: 514: 515:
516: public function registerAuthorizationNotification($amount)
517: {
518: return ($this->_isTransactionExists()) ? $this : $this->_authorize(false, $amount);
519: }
520:
521: 522: 523: 524: 525: 526:
527: public function pay($invoice)
528: {
529: $this->_updateTotals(array(
530: 'amount_paid' => $invoice->getGrandTotal(),
531: 'base_amount_paid' => $invoice->getBaseGrandTotal(),
532: 'shipping_captured' => $invoice->getShippingAmount(),
533: 'base_shipping_captured' => $invoice->getBaseShippingAmount(),
534: ));
535: Mage::dispatchEvent('sales_order_payment_pay', array('payment' => $this, 'invoice' => $invoice));
536: return $this;
537: }
538:
539: 540: 541: 542: 543: 544:
545: public function cancelInvoice($invoice)
546: {
547: $this->_updateTotals(array(
548: 'amount_paid' => -1 * $invoice->getGrandTotal(),
549: 'base_amount_paid' => -1 * $invoice->getBaseGrandTotal(),
550: 'shipping_captured' => -1 * $invoice->getShippingAmount(),
551: 'base_shipping_captured' => -1 * $invoice->getBaseShippingAmount(),
552: ));
553: Mage::dispatchEvent('sales_order_payment_cancel_invoice', array('payment' => $this, 'invoice' => $invoice));
554: return $this;
555: }
556:
557: 558: 559: 560: 561: 562:
563: protected function _invoice()
564: {
565: $invoice = $this->getOrder()->prepareInvoice();
566:
567: $invoice->register();
568: if ($this->getMethodInstance()->canCapture()) {
569: $invoice->capture();
570: }
571:
572: $this->getOrder()->addRelatedObject($invoice);
573: return $invoice;
574: }
575:
576: 577: 578: 579: 580:
581: public function canVoid(Varien_Object $document)
582: {
583: if (null === $this->_canVoidLookup) {
584: $this->_canVoidLookup = (bool)$this->getMethodInstance()->canVoid($document);
585: if ($this->_canVoidLookup) {
586: $authTransaction = $this->getAuthorizationTransaction();
587: $this->_canVoidLookup = (bool)$authTransaction && !(int)$authTransaction->getIsClosed();
588: }
589: }
590: return $this->_canVoidLookup;
591: }
592:
593: 594: 595: 596: 597: 598: 599:
600: public function void(Varien_Object $document)
601: {
602: $this->_void(true);
603: Mage::dispatchEvent('sales_order_payment_void', array('payment' => $this, 'invoice' => $document));
604: return $this;
605: }
606:
607: 608: 609: 610: 611: 612: 613:
614: public function registerVoidNotification($amount = null)
615: {
616: if (!$this->hasMessage()) {
617: $this->setMessage(Mage::helper('sales')->__('Registered a Void notification.'));
618: }
619: return $this->_void(false, $amount);
620: }
621:
622: 623: 624: 625: 626: 627: 628: 629:
630: public function refund($creditmemo)
631: {
632: $baseAmountToRefund = $this->_formatAmount($creditmemo->getBaseGrandTotal());
633: $order = $this->getOrder();
634:
635: $this->_generateTransactionId(Mage_Sales_Model_Order_Payment_Transaction::TYPE_REFUND);
636:
637:
638: $isOnline = false;
639: $gateway = $this->getMethodInstance();
640: $invoice = null;
641: if ($gateway->canRefund() && $creditmemo->getDoTransaction()) {
642: $this->setCreditmemo($creditmemo);
643: $invoice = $creditmemo->getInvoice();
644: if ($invoice) {
645: $isOnline = true;
646: $captureTxn = $this->_lookupTransaction($invoice->getTransactionId());
647: if ($captureTxn) {
648: $this->setParentTransactionId($captureTxn->getTxnId());
649: }
650: $this->setShouldCloseParentTransaction(true);
651: try {
652: $gateway->setStore($this->getOrder()->getStoreId())
653: ->processBeforeRefund($invoice, $this)
654: ->refund($this, $baseAmountToRefund)
655: ->processCreditmemo($creditmemo, $this)
656: ;
657: } catch (Mage_Core_Exception $e) {
658: if (!$captureTxn) {
659: $e->setMessage(' ' . Mage::helper('sales')->__('If the invoice was created offline, try creating an offline creditmemo.'), true);
660: }
661: throw $e;
662: }
663: }
664: }
665:
666:
667: $this->_updateTotals(array(
668: 'amount_refunded' => $creditmemo->getGrandTotal(),
669: 'base_amount_refunded' => $baseAmountToRefund,
670: 'base_amount_refunded_online' => $isOnline ? $baseAmountToRefund : null,
671: 'shipping_refunded' => $creditmemo->getShippingAmount(),
672: 'base_shipping_refunded' => $creditmemo->getBaseShippingAmount(),
673: ));
674:
675:
676: $transaction = $this->_addTransaction(
677: Mage_Sales_Model_Order_Payment_Transaction::TYPE_REFUND,
678: $creditmemo,
679: $isOnline
680: );
681: if ($invoice) {
682: $message = Mage::helper('sales')->__('Refunded amount of %s online.', $this->_formatPrice($baseAmountToRefund));
683: } else {
684: $message = $this->hasMessage() ? $this->getMessage()
685: : Mage::helper('sales')->__('Refunded amount of %s offline.', $this->_formatPrice($baseAmountToRefund));
686: }
687: $message = $message = $this->_prependMessage($message);
688: $message = $this->_appendTransactionToMessage($transaction, $message);
689: $order->setState(Mage_Sales_Model_Order::STATE_PROCESSING, true, $message);
690:
691: Mage::dispatchEvent('sales_order_payment_refund', array('payment' => $this, 'creditmemo' => $creditmemo));
692: return $this;
693: }
694:
695: 696: 697: 698: 699: 700: 701: 702: 703: 704: 705: 706:
707: public function registerRefundNotification($amount)
708: {
709: $notificationAmount = $amount;
710: $this->_generateTransactionId(Mage_Sales_Model_Order_Payment_Transaction::TYPE_REFUND,
711: $this->_lookupTransaction($this->getParentTransactionId())
712: );
713: if ($this->_isTransactionExists()) {
714: return $this;
715: }
716: $order = $this->getOrder();
717: $invoice = $this->_getInvoiceForTransactionId($this->getParentTransactionId());
718:
719: if ($invoice) {
720: $baseGrandTotal = $invoice->getBaseGrandTotal();
721: $amountRefundLeft = $baseGrandTotal - $invoice->getBaseTotalRefunded();
722: } else {
723: $baseGrandTotal = $order->getBaseGrandTotal();
724: $amountRefundLeft = $baseGrandTotal - $order->getBaseTotalRefunded();
725: }
726:
727: if ($amountRefundLeft < $amount) {
728: $amount = $amountRefundLeft;
729: }
730:
731: if ($amount <= 0) {
732: $order->addStatusHistoryComment(Mage::helper('sales')->__('IPN "Refunded". Refund issued by merchant. Registered notification about refunded amount of %s. Transaction ID: "%s"', $this->_formatPrice($notificationAmount), $this->getTransactionId()), false);
733: return $this;
734: }
735:
736: $serviceModel = Mage::getModel('sales/service_order', $order);
737: if ($invoice) {
738: if ($invoice->getBaseTotalRefunded() > 0) {
739: $adjustment = array('adjustment_positive' => $amount);
740: } else {
741: $adjustment = array('adjustment_negative' => $baseGrandTotal - $amount);
742: }
743: $creditmemo = $serviceModel->prepareInvoiceCreditmemo($invoice, $adjustment);
744: if ($creditmemo) {
745: $totalRefunded = $invoice->getBaseTotalRefunded() + $creditmemo->getBaseGrandTotal();
746: $this->setShouldCloseParentTransaction($invoice->getBaseGrandTotal() <= $totalRefunded);
747: }
748: } else {
749: if ($order->getBaseTotalRefunded() > 0) {
750: $adjustment = array('adjustment_positive' => $amount);
751: } else {
752: $adjustment = array('adjustment_negative' => $baseGrandTotal - $amount);
753: }
754: $creditmemo = $serviceModel->prepareCreditmemo($adjustment);
755: if ($creditmemo) {
756: $totalRefunded = $order->getBaseTotalRefunded() + $creditmemo->getBaseGrandTotal();
757: $this->setShouldCloseParentTransaction($order->getBaseGrandTotal() <= $totalRefunded);
758: }
759: }
760:
761: $creditmemo->setPaymentRefundDisallowed(true)
762: ->setAutomaticallyCreated(true)
763: ->register()
764: ->addComment(Mage::helper('sales')->__('Credit memo has been created automatically'))
765: ->save();
766:
767: $this->_updateTotals(array(
768: 'amount_refunded' => $creditmemo->getGrandTotal(),
769: 'base_amount_refunded_online' => $amount
770: ));
771:
772: $this->setCreatedCreditmemo($creditmemo);
773:
774: $transaction = $this->_addTransaction(Mage_Sales_Model_Order_Payment_Transaction::TYPE_REFUND, $creditmemo);
775: $message = $this->_prependMessage(
776: Mage::helper('sales')->__('Registered notification about refunded amount of %s.', $this->_formatPrice($amount))
777: );
778: $message = $this->_appendTransactionToMessage($transaction, $message);
779: $order->setState(Mage_Sales_Model_Order::STATE_PROCESSING, true, $message);
780: return $this;
781: }
782:
783: 784: 785: 786: 787: 788:
789: public function cancelCreditmemo($creditmemo)
790: {
791: $this->_updateTotals(array(
792: 'amount_refunded' => -1 * $creditmemo->getGrandTotal(),
793: 'base_amount_refunded' => -1 * $creditmemo->getBaseGrandTotal(),
794: 'shipping_refunded' => -1 * $creditmemo->getShippingAmount(),
795: 'base_shipping_refunded' => -1 * $creditmemo->getBaseShippingAmount()
796: ));
797: Mage::dispatchEvent('sales_order_payment_cancel_creditmemo',
798: array('payment' => $this, 'creditmemo' => $creditmemo)
799: );
800: return $this;
801: }
802:
803: 804: 805: 806: 807:
808: public function cancel()
809: {
810: $isOnline = true;
811: if (!$this->canVoid(new Varien_Object())) {
812: $isOnline = false;
813: }
814:
815: if (!$this->hasMessage()) {
816: $this->setMessage($isOnline ? Mage::helper('sales')->__('Canceled order online.')
817: : Mage::helper('sales')->__('Canceled order offline.')
818: );
819: }
820:
821: if ($isOnline) {
822: $this->_void($isOnline, null, 'cancel');
823: }
824:
825: Mage::dispatchEvent('sales_order_payment_cancel', array('payment' => $this));
826:
827: return $this;
828: }
829:
830: 831: 832: 833: 834:
835: public function canReviewPayment()
836: {
837: return (bool)$this->getMethodInstance()->canReviewPayment($this);
838: }
839:
840: public function canFetchTransactionInfo()
841: {
842: return (bool)$this->getMethodInstance()->canFetchTransactionInfo();
843: }
844:
845: 846: 847: 848: 849:
850: public function accept()
851: {
852: $this->registerPaymentReviewAction(self::REVIEW_ACTION_ACCEPT, true);
853: return $this;
854: }
855:
856: 857: 858: 859: 860:
861: public function deny()
862: {
863: $this->registerPaymentReviewAction(self::REVIEW_ACTION_DENY, true);
864: return $this;
865: }
866:
867: 868: 869: 870: 871: 872: 873: 874: 875:
876: public function registerPaymentReviewAction($action, $isOnline)
877: {
878: $order = $this->getOrder();
879:
880: $transactionId = $isOnline ? $this->getLastTransId() : $this->getTransactionId();
881: $invoice = $this->_getInvoiceForTransactionId($transactionId);
882:
883:
884: $result = null; $message = null;
885: switch ($action) {
886: case self::REVIEW_ACTION_ACCEPT:
887: if ($isOnline) {
888: if ($this->getMethodInstance()->setStore($order->getStoreId())->acceptPayment($this)) {
889: $result = true;
890: $message = Mage::helper('sales')->__('Approved the payment online.');
891: } else {
892: $result = -1;
893: $message = Mage::helper('sales')->__('There is no need to approve this payment.');
894: }
895: } else {
896: $result = (bool)$this->getNotificationResult() ? true : -1;
897: $message = Mage::helper('sales')->__('Registered notification about approved payment.');
898: }
899: break;
900: case self::REVIEW_ACTION_DENY:
901: if ($isOnline) {
902: if ($this->getMethodInstance()->setStore($order->getStoreId())->denyPayment($this)) {
903: $result = false;
904: $message = Mage::helper('sales')->__('Denied the payment online.');
905: } else {
906: $result = -1;
907: $message = Mage::helper('sales')->__('There is no need to deny this payment.');
908: }
909: } else {
910: $result = (bool)$this->getNotificationResult() ? false : -1;
911: $message = Mage::helper('sales')->__('Registered notification about denied payment.');
912: }
913: break;
914: case self::REVIEW_ACTION_UPDATE:
915: if ($isOnline) {
916: $this->getMethodInstance()
917: ->setStore($order->getStoreId())
918: ->fetchTransactionInfo($this, $transactionId);
919: } else {
920:
921: }
922: if ($this->getIsTransactionApproved()) {
923: $result = true;
924: $message = Mage::helper('sales')->__('Registered update about approved payment.');
925: } elseif ($this->getIsTransactionDenied()) {
926: $result = false;
927: $message = Mage::helper('sales')->__('Registered update about denied payment.');
928: } else {
929: $result = -1;
930: $message = Mage::helper('sales')->__('There is no update for the payment.');
931: }
932: break;
933: default:
934: throw new Exception('Not implemented.');
935: }
936: $message = $this->_prependMessage($message);
937: if ($transactionId) {
938: $message = $this->_appendTransactionToMessage($transactionId, $message);
939: }
940:
941:
942: if (-1 === $result) {
943: $order->addStatusHistoryComment($message);
944: } elseif (true === $result) {
945: if ($invoice) {
946: $invoice->pay();
947: $this->_updateTotals(array('base_amount_paid_online' => $invoice->getBaseGrandTotal()));
948: $order->addRelatedObject($invoice);
949: }
950: $order->setState(Mage_Sales_Model_Order::STATE_PROCESSING, true, $message);
951: } elseif (false === $result) {
952: if ($invoice) {
953: $invoice->cancel();
954: $order->addRelatedObject($invoice);
955: }
956: $order->registerCancellation($message, false);
957: }
958: return $this;
959: }
960:
961: 962: 963: 964: 965: 966: 967: 968: 969:
970: protected function _order($amount)
971: {
972:
973: $amount = $this->_formatAmount($amount, true);
974:
975:
976: $order = $this->getOrder();
977: $state = Mage_Sales_Model_Order::STATE_PROCESSING;
978: $status = true;
979: $this->getMethodInstance()->setStore($order->getStoreId())->order($this, $amount);
980:
981: if ($this->getSkipOrderProcessing()) {
982: return $this;
983: }
984:
985:
986: if ($this->getIsTransactionPending()) {
987: $message = Mage::helper('sales')->__('Ordering amount of %s is pending approval on gateway.', $this->_formatPrice($amount));
988: $state = Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW;
989: if ($this->getIsFraudDetected()) {
990: $status = Mage_Sales_Model_Order::STATUS_FRAUD;
991: }
992: } else {
993: $message = Mage::helper('sales')->__('Ordered amount of %s.', $this->_formatPrice($amount));
994: }
995:
996:
997: $transaction = $this->_addTransaction(Mage_Sales_Model_Order_Payment_Transaction::TYPE_ORDER);
998: $message = $this->_prependMessage($message);
999: $message = $this->_appendTransactionToMessage($transaction, $message);
1000: $order->setState($state, $status, $message);
1001: return $this;
1002: }
1003:
1004: 1005: 1006: 1007: 1008: 1009: 1010: 1011: 1012: 1013:
1014: protected function _authorize($isOnline, $amount)
1015: {
1016:
1017: $amount = $this->_formatAmount($amount, true);
1018: $this->setBaseAmountAuthorized($amount);
1019:
1020:
1021: $order = $this->getOrder();
1022: $state = Mage_Sales_Model_Order::STATE_PROCESSING;
1023: $status = true;
1024: if ($isOnline) {
1025:
1026: $this->getMethodInstance()->setStore($order->getStoreId())->authorize($this, $amount);
1027: }
1028:
1029:
1030: if ($this->getIsTransactionPending()) {
1031: $message = Mage::helper('sales')->__('Authorizing amount of %s is pending approval on gateway.', $this->_formatPrice($amount));
1032: $state = Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW;
1033: if ($this->getIsFraudDetected()) {
1034: $status = Mage_Sales_Model_Order::STATUS_FRAUD;
1035: }
1036: } else {
1037: $message = Mage::helper('sales')->__('Authorized amount of %s.', $this->_formatPrice($amount));
1038: }
1039:
1040:
1041: $transaction = $this->_addTransaction(Mage_Sales_Model_Order_Payment_Transaction::TYPE_AUTH);
1042: if ($order->isNominal()) {
1043: $message = $this->_prependMessage(Mage::helper('sales')->__('Nominal order registered.'));
1044: } else {
1045: $message = $this->_prependMessage($message);
1046: $message = $this->_appendTransactionToMessage($transaction, $message);
1047: }
1048: $order->setState($state, $status, $message);
1049:
1050: return $this;
1051: }
1052:
1053: 1054: 1055: 1056: 1057:
1058: public function authorize($isOnline, $amount)
1059: {
1060: return $this->_authorize($isOnline, $amount);
1061: }
1062:
1063: 1064: 1065: 1066: 1067: 1068: 1069: 1070: 1071: 1072: 1073:
1074: protected function _void($isOnline, $amount = null, $gatewayCallback = 'void')
1075: {
1076: $order = $this->getOrder();
1077: $authTransaction = $this->getAuthorizationTransaction();
1078: $this->_generateTransactionId(Mage_Sales_Model_Order_Payment_Transaction::TYPE_VOID, $authTransaction);
1079: $this->setShouldCloseParentTransaction(true);
1080:
1081:
1082: if ($isOnline) {
1083: $this->getMethodInstance()->setStore($order->getStoreId())->$gatewayCallback($this);
1084: }
1085: if ($this->_isTransactionExists()) {
1086: return $this;
1087: }
1088:
1089:
1090:
1091: if ($authTransaction && ($order->getBaseGrandTotal() == $this->getBaseAmountAuthorized())
1092: && (0 == $this->getBaseAmountCanceled())) {
1093: if ($authTransaction->canVoidAuthorizationCompletely()) {
1094: $amount = (float)$order->getBaseGrandTotal();
1095: }
1096: }
1097:
1098: if ($amount) {
1099: $amount = $this->_formatAmount($amount);
1100: }
1101:
1102:
1103: $transaction = $this->_addTransaction(Mage_Sales_Model_Order_Payment_Transaction::TYPE_VOID, null, true);
1104: $message = $this->hasMessage() ? $this->getMessage() : Mage::helper('sales')->__('Voided authorization.');
1105: $message = $this->_prependMessage($message);
1106: if ($amount) {
1107: $message .= ' ' . Mage::helper('sales')->__('Amount: %s.', $this->_formatPrice($amount));
1108: }
1109: $message = $this->_appendTransactionToMessage($transaction, $message);
1110: $order->setState(Mage_Sales_Model_Order::STATE_PROCESSING, true, $message);
1111: return $this;
1112: }
1113:
1114:
1115:
1116:
1117:
1118:
1119:
1120:
1121:
1122:
1123: 1124: 1125: 1126: 1127: 1128: 1129: 1130: 1131: 1132: 1133: 1134: 1135: 1136: 1137: 1138: 1139: 1140: 1141: 1142: 1143: 1144:
1145: protected function _addTransaction($type, $salesDocument = null, $failsafe = false)
1146: {
1147: if ($this->getSkipTransactionCreation()) {
1148: $this->unsTransactionId();
1149: return null;
1150: }
1151:
1152:
1153: $transactionId = $this->getTransactionId();
1154: if (null !== $transactionId) {
1155:
1156: $transaction = false;
1157: if ($this->getOrder()->getId()) {
1158: $transaction = $this->_lookupTransaction($transactionId);
1159: }
1160: if (!$transaction) {
1161: $transaction = Mage::getModel('sales/order_payment_transaction')->setTxnId($transactionId);
1162: }
1163: $transaction
1164: ->setOrderPaymentObject($this)
1165: ->setTxnType($type)
1166: ->isFailsafe($failsafe);
1167:
1168: if ($this->hasIsTransactionClosed()) {
1169: $transaction->setIsClosed((int)$this->getIsTransactionClosed());
1170: }
1171:
1172:
1173: if ($this->_transactionAdditionalInfo) {
1174: foreach ($this->_transactionAdditionalInfo as $key => $value) {
1175: $transaction->setAdditionalInformation($key, $value);
1176: }
1177: }
1178:
1179:
1180: $this->setLastTransId($transactionId);
1181: $this->setCreatedTransaction($transaction);
1182: $this->getOrder()->addRelatedObject($transaction);
1183: if ($salesDocument && $salesDocument instanceof Mage_Sales_Model_Abstract) {
1184: $salesDocument->setTransactionId($transactionId);
1185:
1186: }
1187:
1188:
1189: $parentTransactionId = $this->getParentTransactionId();
1190:
1191: if ($parentTransactionId) {
1192: $transaction->setParentTxnId($parentTransactionId);
1193: if ($this->getShouldCloseParentTransaction()) {
1194: $parentTransaction = $this->_lookupTransaction($parentTransactionId);
1195: if ($parentTransaction) {
1196: if (!$parentTransaction->getIsClosed()) {
1197: $parentTransaction->isFailsafe($failsafe)->close(false);
1198: }
1199: $this->getOrder()->addRelatedObject($parentTransaction);
1200: }
1201: }
1202: }
1203: return $transaction;
1204: }
1205: }
1206:
1207: 1208: 1209: 1210: 1211: 1212: 1213: 1214: 1215:
1216: public function addTransaction($type, $salesDocument = null, $failsafe = false, $message = false)
1217: {
1218: $transaction = $this->_addTransaction($type, $salesDocument, $failsafe);
1219:
1220: if ($message) {
1221: $order = $this->getOrder();
1222: $message = $this->_appendTransactionToMessage($transaction, $message);
1223: $order->addStatusHistoryComment($message);
1224: }
1225:
1226: return $transaction;
1227: }
1228:
1229: 1230: 1231: 1232: 1233: 1234:
1235: public function importTransactionInfo(Mage_Sales_Model_Order_Payment_Transaction $transactionTo)
1236: {
1237: $data = $this->getMethodInstance()
1238: ->setStore($this->getOrder()->getStoreId())
1239: ->fetchTransactionInfo($this, $transactionTo->getTxnId());
1240: if ($data) {
1241: $transactionTo->setAdditionalInformation(Mage_Sales_Model_Order_Payment_Transaction::RAW_DETAILS, $data);
1242: }
1243: return $this;
1244: }
1245:
1246: 1247: 1248: 1249: 1250:
1251: public function getBillingAgreement()
1252: {
1253: return $this->_billingAgreement;
1254: }
1255:
1256: 1257: 1258: 1259: 1260: 1261:
1262: protected function _updateTotals($data)
1263: {
1264: foreach ($data as $key => $amount) {
1265: if (null !== $amount) {
1266: $was = $this->getDataUsingMethod($key);
1267: $this->setDataUsingMethod($key, $was + $amount);
1268: }
1269: }
1270: }
1271:
1272: 1273: 1274: 1275: 1276: 1277: 1278: 1279:
1280: protected function _avoidDoubleTransactionProcessing($txnId = null)
1281: {
1282: if ($this->_isTransactionExists($txnId)) {
1283: Mage::throwException(
1284: Mage::helper('sales')->__('Transaction "%s" was already processed.', $txnId)
1285: );
1286: }
1287: }
1288:
1289: 1290: 1291: 1292: 1293: 1294:
1295: protected function _isTransactionExists($txnId = null)
1296: {
1297: if (null === $txnId) {
1298: $txnId = $this->getTransactionId();
1299: }
1300: return $txnId && $this->_lookupTransaction($txnId);
1301: }
1302:
1303: 1304: 1305: 1306: 1307: 1308: 1309:
1310: protected function _appendTransactionToMessage($transaction, $message)
1311: {
1312: if ($transaction) {
1313: $txnId = is_object($transaction) ? $transaction->getTxnId() : $transaction;
1314: $message .= ' ' . Mage::helper('sales')->__('Transaction ID: "%s".', $txnId);
1315: }
1316: return $message;
1317: }
1318:
1319: 1320: 1321: 1322: 1323: 1324: 1325:
1326: protected function _prependMessage($messagePrependTo)
1327: {
1328: $preparedMessage = $this->getPreparedMessage();
1329: if ($preparedMessage) {
1330: if (is_string($preparedMessage)) {
1331: return $preparedMessage . ' ' . $messagePrependTo;
1332: } elseif (is_object($preparedMessage)
1333: && ($preparedMessage instanceof Mage_Sales_Model_Order_Status_History)
1334: ) {
1335: $comment = $preparedMessage->getComment() . ' ' . $messagePrependTo;
1336: $preparedMessage->setComment($comment);
1337: return $comment;
1338: }
1339: }
1340: return $messagePrependTo;
1341: }
1342:
1343: 1344: 1345: 1346: 1347: 1348: 1349:
1350: protected function _formatAmount($amount, $asFloat = false)
1351: {
1352: $amount = Mage::app()->getStore()->roundPrice($amount);
1353: return !$asFloat ? (string)$amount : $amount;
1354: }
1355:
1356: 1357: 1358: 1359: 1360:
1361: protected function _formatPrice($amount)
1362: {
1363: return $this->getOrder()->getBaseCurrency()->formatTxt($amount);
1364: }
1365:
1366: 1367: 1368: 1369: 1370: 1371:
1372: protected function _lookupTransaction($txnId, $txnType = false)
1373: {
1374: if (!$txnId) {
1375: if ($txnType && $this->getId()) {
1376: $collection = Mage::getModel('sales/order_payment_transaction')->getCollection()
1377: ->setOrderFilter($this->getOrder())
1378: ->addPaymentIdFilter($this->getId())
1379: ->addTxnTypeFilter($txnType)
1380: ->setOrder('created_at', Varien_Data_Collection::SORT_ORDER_DESC)
1381: ->setOrder('transaction_id', Varien_Data_Collection::SORT_ORDER_DESC);
1382: foreach ($collection as $txn) {
1383: $txn->setOrderPaymentObject($this);
1384: $this->_transactionsLookup[$txn->getTxnId()] = $txn;
1385: return $txn;
1386: }
1387: }
1388: return false;
1389: }
1390: if (isset($this->_transactionsLookup[$txnId])) {
1391: return $this->_transactionsLookup[$txnId];
1392: }
1393: $txn = Mage::getModel('sales/order_payment_transaction')
1394: ->setOrderPaymentObject($this)
1395: ->loadByTxnId($txnId);
1396: if ($txn->getId()) {
1397: $this->_transactionsLookup[$txnId] = $txn;
1398: } else {
1399: $this->_transactionsLookup[$txnId] = false;
1400: }
1401: return $this->_transactionsLookup[$txnId];
1402: }
1403:
1404: 1405: 1406: 1407: 1408: 1409:
1410: public function lookupTransaction($txnId, $txnType = false)
1411: {
1412: return $this->_lookupTransaction($txnId, $txnType);
1413: }
1414:
1415: 1416: 1417: 1418:
1419: public function getAuthorizationTransaction()
1420: {
1421: if ($this->getParentTransactionId()) {
1422: $txn = $this->_lookupTransaction($this->getParentTransactionId());
1423: } else {
1424: $txn = false;
1425: }
1426:
1427: if (!$txn) {
1428: $txn = $this->_lookupTransaction(false, Mage_Sales_Model_Order_Payment_Transaction::TYPE_AUTH);
1429: }
1430: return $txn;
1431: }
1432:
1433: 1434: 1435: 1436: 1437:
1438: public function getTransaction($transactionId)
1439: {
1440: return $this->_lookupTransaction($transactionId);
1441: }
1442:
1443: 1444: 1445: 1446: 1447: 1448: 1449:
1450: protected function _generateTransactionId($type, $transactionBasedOn = false)
1451: {
1452: if (!$this->getParentTransactionId() && !$this->getTransactionId() && $transactionBasedOn) {
1453: $this->setParentTransactionId($transactionBasedOn->getTxnId());
1454: }
1455:
1456: if (($parentTxnId = $this->getParentTransactionId()) && !$this->getTransactionId()) {
1457: $this->setTransactionId("{$parentTxnId}-{$type}");
1458: }
1459: }
1460:
1461: 1462: 1463: 1464: 1465:
1466: protected function _isCaptureFinal($amountToCapture)
1467: {
1468: $amountToCapture = $this->_formatAmount($amountToCapture, true);
1469: $orderGrandTotal = $this->_formatAmount($this->getOrder()->getBaseGrandTotal(), true);
1470: if ($orderGrandTotal == $this->_formatAmount($this->getBaseAmountPaid(), true) + $amountToCapture) {
1471: if (false !== $this->getShouldCloseParentTransaction()) {
1472: $this->setShouldCloseParentTransaction(true);
1473: }
1474: return true;
1475: }
1476: return false;
1477: }
1478:
1479: 1480: 1481: 1482: 1483:
1484: protected function _beforeSave()
1485: {
1486: parent::_beforeSave();
1487:
1488: if (!$this->getParentId() && $this->getOrder()) {
1489: $this->setParentId($this->getOrder()->getId());
1490: }
1491:
1492: return $this;
1493: }
1494:
1495: 1496: 1497: 1498:
1499: protected function _createBillingAgreement()
1500: {
1501: if ($this->getBillingAgreementData()) {
1502: $order = $this->getOrder();
1503: $agreement = Mage::getModel('sales/billing_agreement')->importOrderPayment($this);
1504: if ($agreement->isValid()) {
1505: $message = Mage::helper('sales')->__('Created billing agreement #%s.', $agreement->getReferenceId());
1506: $order->addRelatedObject($agreement);
1507: $this->_billingAgreement = $agreement;
1508: } else {
1509: $message = Mage::helper('sales')->__('Failed to create billing agreement for this order.');
1510: }
1511: $comment = $order->addStatusHistoryComment($message);
1512: $order->addRelatedObject($comment);
1513: }
1514: }
1515:
1516: 1517: 1518: 1519: 1520: 1521:
1522: public function setTransactionAdditionalInfo($key, $value)
1523: {
1524: if (is_array($key)) {
1525: $this->_transactionAdditionalInfo = $key;
1526: } else {
1527: $this->_transactionAdditionalInfo[$key] = $value;
1528: }
1529: }
1530:
1531: 1532: 1533: 1534: 1535: 1536:
1537: public function getTransactionAdditionalInfo($key = null)
1538: {
1539: if (is_null($key)) {
1540: return $this->_transactionAdditionalInfo;
1541: }
1542: return isset($this->_transactionAdditionalInfo[$key]) ? $this->_transactionAdditionalInfo[$key] : null;
1543: }
1544:
1545: 1546: 1547: 1548: 1549:
1550: public function resetTransactionAdditionalInfo()
1551: {
1552: $this->_transactionAdditionalInfo = array();
1553: return $this;
1554: }
1555:
1556: 1557: 1558: 1559: 1560: 1561:
1562: protected function _getInvoiceForTransactionId($transactionId)
1563: {
1564: foreach ($this->getOrder()->getInvoiceCollection() as $invoice) {
1565: if ($invoice->getTransactionId() == $transactionId) {
1566: $invoice->load($invoice->getId());
1567: return $invoice;
1568: }
1569: }
1570: foreach ($this->getOrder()->getInvoiceCollection() as $invoice) {
1571: if ($invoice->getState() == Mage_Sales_Model_Order_Invoice::STATE_OPEN
1572: && $invoice->load($invoice->getId())
1573: ) {
1574: $invoice->setTransactionId($transactionId);
1575: return $invoice;
1576: }
1577: }
1578: return false;
1579: }
1580: }
1581: