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: class Mage_Sales_Model_Order_Payment_Transaction extends Mage_Core_Model_Abstract
52: {
53: 54: 55: 56:
57: const TYPE_PAYMENT = 'payment';
58: const TYPE_ORDER = 'order';
59: const TYPE_AUTH = 'authorization';
60: const TYPE_CAPTURE = 'capture';
61: const TYPE_VOID = 'void';
62: const TYPE_REFUND = 'refund';
63:
64: 65: 66: 67:
68: const RAW_DETAILS = 'raw_details_info';
69:
70: 71: 72: 73:
74: protected $_paymentObject = null;
75:
76:
77: 78: 79: 80: 81:
82: protected $_order = null;
83:
84: 85: 86: 87:
88: protected $_parentTransaction = null;
89:
90: 91: 92: 93:
94: protected $_children = null;
95:
96: 97: 98: 99: 100: 101:
102: protected $_identifiedChildren = null;
103:
104: 105: 106: 107:
108: protected $_transactionsAutoLinking = true;
109:
110: 111: 112: 113: 114:
115: protected $_isFailsafe = false;
116:
117: 118: 119: 120:
121: protected $_hasChild = null;
122:
123: 124: 125: 126: 127: 128:
129: protected $_eventPrefix = 'sales_order_payment_transaction';
130:
131: 132: 133: 134: 135: 136:
137: protected $_eventObject = 'order_payment_transaction';
138:
139: 140: 141: 142: 143:
144: protected $_orderWebsiteId = null;
145:
146: 147: 148:
149: protected function _construct()
150: {
151: $this->_init('sales/order_payment_transaction');
152: return parent::_construct();
153: }
154:
155: 156: 157: 158: 159:
160: public function setOrderPaymentObject(Mage_Sales_Model_Order_Payment $payment)
161: {
162: $this->_paymentObject = $payment;
163: $this->setOrder($payment->getOrder());
164: return $this;
165: }
166:
167: 168: 169: 170: 171:
172: public function setTxnId($txnId)
173: {
174: $this->_verifyTxnId($txnId);
175: return $this->setData('txn_id', $txnId);
176: }
177:
178: 179: 180: 181: 182: 183: 184: 185:
186: public function setParentTxnId($parentTxnId, $txnId = null)
187: {
188: $this->_verifyTxnId($parentTxnId);
189: if (empty($txnId)) {
190: if ('' == $this->getTxnId()) {
191: Mage::throwException(
192: Mage::helper('sales')->__('Parent transaction ID must have a transaction ID.')
193: );
194: }
195: } else {
196: $this->setTxnId($txnId);
197: }
198: return $this->setData('parent_txn_id', $parentTxnId);
199: }
200:
201: 202: 203: 204: 205: 206:
207: public function setTxnType($txnType)
208: {
209: $this->_verifyTxnType($txnType);
210: return $this->setData('txn_type', $txnType);
211: }
212:
213: 214: 215: 216: 217: 218: 219:
220: public function getParentTransaction($shouldLoad = true)
221: {
222: if (null === $this->_parentTransaction) {
223: $this->_verifyThisTransactionExists();
224: $this->_parentTransaction = false;
225: $parentId = $this->getParentId();
226: if ($parentId) {
227: $class = get_class($this);
228: $this->_parentTransaction = new $class;
229: if ($shouldLoad) {
230: $this->_parentTransaction
231: ->setOrderPaymentObject($this->_paymentObject)
232: ->load($parentId);
233: if (!$this->_parentTransaction->getId()) {
234: $this->_parentTransaction = false;
235: } else {
236: $this->_parentTransaction
237: ->hasChildTransaction(true);
238: }
239: }
240: }
241: }
242: return $this->_parentTransaction;
243: }
244:
245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256:
257: public function getChildTransactions($types = null, $txnId = null, $recursive = false)
258: {
259: $this->_loadChildren();
260:
261:
262: if (empty($types) && null === $txnId) {
263: return $this->_children;
264: } elseif ($types && !is_array($types)) {
265: $types = array($types);
266: }
267:
268:
269: if ($txnId) {
270: if (empty($this->_children)) {
271: return;
272: }
273: $transaction = null;
274: if ($this->_identifiedChildren) {
275: if (isset($this->_identifiedChildren[$txnId])) {
276: $transaction = $this->_identifiedChildren[$txnId];
277: }
278: } else {
279: foreach ($this->_children as $child) {
280: if ($child->getTxnId() === $tnxId) {
281: $transaction = $child;
282: break;
283: }
284: }
285: }
286:
287: if (!$transaction || $types && !in_array($transaction->getType(), $types, true)) {
288: return;
289: }
290: return $transaction;
291: }
292:
293:
294: $result = array();
295: foreach ($this->_children as $child) {
296: if (in_array($child->getType(), $types, true)) {
297: $result[$child->getId()] = $child;
298: }
299: }
300: return $result;
301: }
302:
303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313:
314: public function closeAuthorization($shouldSave = true, $dryRun = false)
315: {
316: try {
317: $this->_verifyThisTransactionExists();
318: } catch (Exception $e) {
319: if ($dryRun) {
320: return false;
321: }
322: throw $e;
323: }
324: $authTransaction = false;
325: switch ($this->getTxnType()) {
326: case self::TYPE_VOID:
327:
328: case self::TYPE_CAPTURE:
329: $authTransaction = $this->getParentTransaction();
330: break;
331: case self::TYPE_AUTH:
332: $authTransaction = $this;
333: break;
334:
335: }
336: if ($authTransaction) {
337: if (!$dryRun) {
338: $authTransaction->close($shouldSave);
339: }
340: }
341: return $authTransaction;
342: }
343:
344: 345: 346: 347: 348: 349: 350:
351: public function closeCapture($shouldSave = true)
352: {
353: $this->_verifyThisTransactionExists();
354: $captureTransaction = false;
355: switch ($this->getTxnType()) {
356: case self::TYPE_CAPTURE:
357: $captureTransaction = $this;
358: break;
359: case self::TYPE_REFUND:
360: $captureTransaction = $this->getParentTransaction();
361: break;
362: }
363: if ($captureTransaction) {
364: $captureTransaction->close($shouldSave);
365: }
366: return $captureTransaction;
367: }
368:
369: 370: 371: 372: 373:
374: public function canVoidAuthorizationCompletely()
375: {
376: try {
377: $authTransaction = $this->closeAuthorization('', true);
378: if ($authTransaction->hasChildTransaction() || $this->_children) {
379: return false;
380: }
381: return true;
382: } catch (Mage_Core_Exception $e) {
383:
384: }
385: return false;
386: }
387:
388: 389: 390: 391: 392:
393: public function hasChildTransaction($whetherHasChild = null)
394: {
395: if (null !== $whetherHasChild) {
396: $this->_hasChild = (bool)$whetherHasChild;
397: return $this;
398: }
399: elseif (null === $this->_hasChild) {
400: if ($this->getChildTransactions()) {
401: $this->_hasChild = true;
402: } else {
403: $this->_hasChild = false;
404: }
405: }
406: return $this->_hasChild;
407: }
408:
409: 410: 411: 412: 413:
414: protected function _beforeLoadByTxnId($txnId)
415: {
416: $this->_verifyPaymentObject();
417: Mage::dispatchEvent($this->_eventPrefix . '_load_by_txn_id_before', $this->_getEventData() + array('txn_id' => $txnId));
418: return $this;
419: }
420:
421: 422: 423: 424: 425:
426: public function loadByTxnId($txnId)
427: {
428: $this->_beforeLoadByTxnId($txnId);
429: $this->getResource()->loadObjectByTxnId(
430: $this, $this->getOrderId(), $this->_paymentObject->getId(), $txnId
431: );
432: $this->_afterLoadByTxnId();
433: return $this;
434: }
435:
436: 437: 438: 439: 440:
441: protected function _afterLoadByTxnId()
442: {
443: Mage::dispatchEvent($this->_eventPrefix . '_load_by_txn_id_after', $this->_getEventData());
444: return $this;
445: }
446:
447:
448: 449: 450: 451: 452: 453: 454: 455: 456: 457:
458: public function setAdditionalInformation($key, $value)
459: {
460: if (is_object($value)) {
461: Mage::throwException(Mage::helper('sales')->__('Payment transactions disallow storing objects.'));
462: }
463: $info = $this->_getData('additional_information');
464: if (!$info) {
465: $info = array();
466: }
467: $info[$key] = $value;
468: return $this->setData('additional_information', $info);
469: }
470:
471: 472: 473: 474: 475:
476: public function getAdditionalInformation($key = null)
477: {
478: $info = $this->_getData('additional_information');
479: if (!$info) {
480: $info = array();
481: }
482: if ($key) {
483: return (isset($info[$key]) ? $info[$key] : null);
484: }
485: return $info;
486: }
487:
488: 489: 490: 491: 492:
493: public function unsAdditionalInformation($key = null)
494: {
495: if ($key) {
496: $info = $this->_getData('additional_information');
497: if (is_array($info)) {
498: unset($info[$key]);
499: }
500: } else {
501: $info = array();
502: }
503: return $this->setData('additional_information', $info);
504: }
505:
506: 507: 508: 509: 510: 511:
512: public function close($shouldSave = true)
513: {
514: if (!$this->_isFailsafe) {
515: $this->_verifyThisTransactionExists();
516: }
517: if (1 == $this->getIsClosed() && $this->_isFailsafe) {
518: Mage::throwException(Mage::helper('sales')->__('The transaction "%s" (%s) is already closed.', $this->getTxnId(), $this->getTxnType()));
519: }
520: $this->setIsClosed(1);
521: if ($shouldSave) {
522: $this->save();
523: }
524: if ($this->_transactionsAutoLinking && self::TYPE_AUTH === $this->getTxnType()) {
525: try {
526: $paymentTransaction = $this->getParentTransaction();
527: if ($paymentTransaction) {
528: $paymentTransaction->close($shouldSave);
529: }
530: } catch (Exception $e) {
531: if (!$this->_isFailsafe) {
532: throw $e;
533: }
534: }
535: }
536: return $this;
537: }
538:
539: 540: 541: 542: 543: 544:
545: public function getOrderPaymentObject($shouldLoad = true)
546: {
547: $this->_verifyThisTransactionExists();
548: if (null === $this->_paymentObject && $shouldLoad) {
549: $payment = Mage::getModel('sales/order_payment')->load($this->getPaymentId());
550: if ($payment->getId()) {
551: $this->setOrderPaymentObject($payment);
552: }
553: }
554: return $this->_paymentObject;
555: }
556:
557: 558: 559: 560: 561:
562: public function getOrderId()
563: {
564: $orderId = $this->_getData('order_id');
565: if ($orderId) {
566: return $orderId;
567: }
568: if ($this->_paymentObject) {
569: return $this->_paymentObject->getOrder()
570: ? $this->_paymentObject->getOrder()->getId()
571: : $this->_paymentObject->getParentId();
572: }
573: }
574:
575: 576: 577: 578: 579:
580: public function getOrder()
581: {
582: if ($this->_order === null) {
583: $this->setOrder();
584: }
585:
586: return $this->_order;
587: }
588:
589: 590: 591: 592: 593: 594: 595:
596: public function setOrder($order = null)
597: {
598: if (null === $order || $order === true) {
599: if (null !== $this->_paymentObject && $this->_paymentObject->getOrder()) {
600: $this->_order = $this->_paymentObject->getOrder();
601: } elseif ($this->getOrderId() && $order === null) {
602: $this->_order = Mage::getModel('sales/order')->load($this->getOrderId());
603: } else {
604: $this->_order = false;
605: }
606: } elseif (!$this->getId() || ($this->getOrderId() == $order->getId())) {
607: $this->_order = $order;
608: } else {
609: Mage::throwException(Mage::helper('sales')->__('Set order for existing transactions not allowed'));
610: }
611:
612: return $this;
613: }
614:
615: 616: 617: 618: 619:
620: public function isFailsafe($setFailsafe = null)
621: {
622: if (null === $setFailsafe) {
623: return $this->_isFailsafe;
624: }
625: $this->_isFailsafe = (bool)$setFailsafe;
626: return $this;
627: }
628:
629: 630: 631: 632: 633:
634: protected function _beforeSave()
635: {
636:
637: $this->_verifyPaymentObject();
638: if (!$this->getId()) {
639:
640: if (null !== $this->_paymentObject) {
641: $this->setPaymentId($this->_paymentObject->getId());
642: }
643:
644: if (null !== $this->_order) {
645: $this->setOrderId($this->_order->getId());
646: }
647:
648: $this->setCreatedAt(Mage::getModel('core/date')->gmtDate());
649: }
650: return parent::_beforeSave();
651: }
652:
653: 654: 655: 656:
657: protected function _loadChildren()
658: {
659: if (null !== $this->_children) {
660: return;
661: }
662:
663:
664: $this->_verifyThisTransactionExists();
665: $payment = $this->_verifyPaymentObject(true);
666: $paymentId = $payment ? $payment->getId() : $this->_getData('payment_id');
667: if (!$paymentId) {
668: Mage::throwException(Mage::helper('sales')->__('At least a payment ID must be set.'));
669: }
670:
671: $this->setOrder(true);
672:
673: $orderFilter = $this->getOrder();
674: if (!$orderFilter) {
675: $orderFilter = $this->getOrderId();
676: }
677:
678:
679: $children = $this->getResourceCollection()
680: ->setOrderFilter($orderFilter)
681: ->addPaymentIdFilter($paymentId)
682: ->addParentIdFilter($this->getId());
683:
684:
685: $this->_children = array();
686: $this->_identifiedChildren = array();
687: foreach ($children as $child) {
688: if ($payment) {
689: $child->setOrderPaymentObject($payment);
690: }
691: $this->_children[$child->getId()] = $child;
692: if (false !== $this->_identifiedChildren) {
693: $childTxnId = $child->getTxnId();
694: if (!$childTxnId || '0' == $childTxnId) {
695: $this->_identifiedChildren = false;
696: } else {
697: $this->_identifiedChildren[$child->getTxnId()] = $child;
698: }
699: }
700: }
701: if (false === $this->_identifiedChildren) {
702: $this->_identifiedChildren = array();
703: }
704: }
705:
706: 707: 708: 709: 710:
711: protected function _isVoided()
712: {
713: $this->_verifyThisTransactionExists();
714: return self::TYPE_AUTH === $this->getTxnType()
715: && (bool)count($this->getChildTransactions(self::TYPE_VOID));
716: }
717:
718: 719: 720: 721:
722: public function isVoided()
723: {
724: return $this->_isVoided();
725: }
726:
727: 728: 729: 730: 731:
732: public function getTransactionTypes()
733: {
734: return array(
735: Mage_Sales_Model_Order_Payment_Transaction::TYPE_ORDER => Mage::helper('sales')->__('Order'),
736: Mage_Sales_Model_Order_Payment_Transaction::TYPE_AUTH => Mage::helper('sales')->__('Authorization'),
737: Mage_Sales_Model_Order_Payment_Transaction::TYPE_CAPTURE => Mage::helper('sales')->__('Capture'),
738: Mage_Sales_Model_Order_Payment_Transaction::TYPE_VOID => Mage::helper('sales')->__('Void'),
739: Mage_Sales_Model_Order_Payment_Transaction::TYPE_REFUND => Mage::helper('sales')->__('Refund')
740: );
741: }
742:
743: 744: 745: 746: 747:
748: public function getOrderWebsiteId()
749: {
750: if (is_null($this->_orderWebsiteId)) {
751: $this->_orderWebsiteId = (int)$this->getResource()->getOrderWebsiteId($this->getOrderId());
752: }
753: return $this->_orderWebsiteId;
754: }
755:
756: 757: 758: 759: 760:
761: protected function _verifyTxnType($txnType = null)
762: {
763: if (null === $txnType) {
764: $txnType = $this->getTxnType();
765: }
766: switch ($txnType) {
767: case self::TYPE_PAYMENT:
768: case self::TYPE_ORDER:
769: case self::TYPE_AUTH:
770: case self::TYPE_CAPTURE:
771: case self::TYPE_VOID:
772: case self::TYPE_REFUND:
773: break;
774: default:
775: Mage::throwException(Mage::helper('sales')->__('Unsupported transaction type "%s".', $txnType));
776: }
777: }
778:
779: 780: 781: 782: 783: 784: 785:
786: protected function _verifyPaymentObject($dryRun = false)
787: {
788: if (!$this->_paymentObject || !$this->getOrderId()) {
789: if (!$dryRun) {
790: Mage::throwException(Mage::helper('sales')->__('Proper payment object must be set.'));
791: }
792: }
793: return $this->_paymentObject;
794: }
795:
796: 797: 798: 799: 800:
801: protected function _verifyTxnId($txnId)
802: {
803: if (null !== $txnId && 0 == strlen($txnId)) {
804: Mage::throwException(Mage::helper('sales')->__('Transaction ID must not be empty.'));
805: }
806: }
807:
808: 809: 810: 811: 812:
813: protected function _verifyThisTransactionExists()
814: {
815: if (!$this->getId()) {
816: Mage::throwException(Mage::helper('sales')->__('This operation requires an existing transaction object.'));
817: }
818: $this->_verifyTxnType();
819: }
820: }
821: