![]() Server : Apache System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64 User : corals ( 1002) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/corals/old/vendor/magento/module-sales/Model/Order/Payment/ |
<?php /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Sales\Model\Order\Payment; use Magento\Framework\Api\AttributeValueFactory; use Magento\Sales\Api\Data\TransactionInterface; use Magento\Sales\Model\AbstractModel; /** * Payment transaction model * Tracks transaction history, allows to build transactions hierarchy * By default transactions are saved as closed. * * @api * @author Magento Core Team <[email protected]> * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @since 100.0.2 */ class Transaction extends AbstractModel implements TransactionInterface { /** * Raw details key in additional info */ const RAW_DETAILS = 'raw_details_info'; /** * Order instance * * @var \Magento\Sales\Model\Order\Payment */ protected $_order = null; /** * Parent transaction instance * @var \Magento\Sales\Model\Order\Payment\Transaction */ protected $_parentTransaction = null; /** * Child transactions, assoc array of transaction_id => instance * * @var array */ protected $_children = null; /** * Child transactions, assoc array of txn_id => instance * Filled only in case when all child transactions have txn_id * Used for quicker search of child transactions using isset() as opposite to foreaching $_children * * @var array */ protected $_identifiedChildren = null; /** * Whether to perform automatic actions on transactions, such as auto-closing and putting as a parent * * @var bool */ protected $_transactionsAutoLinking = true; /** * Whether to throw exceptions on different operations * * @var bool */ protected $_isFailsafe = false; /** * Whether transaction has children * * @var bool */ protected $_hasChild = null; /** * Event object prefix * * @var string * @see \Magento\Framework\Model\AbstractModel::$_eventPrefix */ protected $_eventPrefix = 'sales_order_payment_transaction'; /** * Event object prefix * * @var string * @see \Magento\Framework\Model\AbstractModel::$_eventObject */ protected $_eventObject = 'order_payment_transaction'; /** * Order website id * * @var int */ protected $_orderWebsiteId = null; /** * @var \Magento\Sales\Model\OrderFactory */ protected $_orderFactory; /** * @var \Magento\Framework\Stdlib\DateTime\DateTimeFactory */ protected $_dateFactory; /** * @var TransactionFactory */ protected $_transactionFactory; /** * @var \Magento\Sales\Api\OrderPaymentRepositoryInterface */ protected $orderPaymentRepository; /** * @var \Magento\Sales\Api\OrderRepositoryInterface */ protected $orderRepository; /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory * @param AttributeValueFactory $customAttributeFactory * @param \Magento\Sales\Model\OrderFactory $orderFactory * @param \Magento\Sales\Api\OrderPaymentRepositoryInterface $orderPaymentRepository * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository * @param \Magento\Framework\Stdlib\DateTime\DateTimeFactory $dateFactory * @param TransactionFactory $transactionFactory * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection * @param array $data * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\Framework\Model\Context $context, \Magento\Framework\Registry $registry, \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory, AttributeValueFactory $customAttributeFactory, \Magento\Sales\Model\OrderFactory $orderFactory, \Magento\Sales\Api\OrderPaymentRepositoryInterface $orderPaymentRepository, \Magento\Sales\Api\OrderRepositoryInterface $orderRepository, \Magento\Framework\Stdlib\DateTime\DateTimeFactory $dateFactory, TransactionFactory $transactionFactory, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, array $data = [] ) { $this->_orderFactory = $orderFactory; $this->_dateFactory = $dateFactory; $this->_transactionFactory = $transactionFactory; $this->orderPaymentRepository = $orderPaymentRepository; $this->orderRepository = $orderRepository; parent::__construct( $context, $registry, $extensionFactory, $customAttributeFactory, $resource, $resourceCollection, $data ); } /** * Initialize resource model * * @return void */ protected function _construct() { $this->_init(\Magento\Sales\Model\ResourceModel\Order\Payment\Transaction::class); parent::_construct(); } /** * Transaction ID setter * * @param string $txnId * @return $this */ public function setTxnId($txnId) { $this->_verifyTxnId($txnId); return $this->setData('txn_id', $txnId); } /** * Parent transaction ID setter Can set the transaction id as well * * @param string $parentTxnId * @param string $txnId * @return $this * @throws \Magento\Framework\Exception\LocalizedException */ public function setParentTxnId($parentTxnId, $txnId = null) { $this->_verifyTxnId($parentTxnId); if (empty($txnId)) { if ('' == $this->getTxnId()) { throw new \Magento\Framework\Exception\LocalizedException( __('The parent transaction ID must have a transaction ID.') ); } } else { $this->setTxnId($txnId); } return $this->setData('parent_txn_id', $parentTxnId); } /** * Transaction type setter * * @param string $txnType * @return $this */ public function setTxnType($txnType) { $this->_verifyTxnType($txnType); return $this->setData('txn_type', $txnType); } /** * Parent transaction getter. May attempt to load it. * * @param bool $shouldLoad * @return bool|\Magento\Sales\Model\Order\Payment\Transaction */ public function getParentTransaction($shouldLoad = true) { if (null === $this->_parentTransaction) { $this->_verifyThisTransactionExists(); $this->_parentTransaction = false; $parentId = $this->getParentId(); if ($parentId) { $this->_parentTransaction = $this->_transactionFactory->create(); if ($shouldLoad) { $this->_parentTransaction ->setOrderId($this->getOrderId()) ->setPaymentId($this->getPaymentId()) ->load($parentId); if (!$this->_parentTransaction->getId()) { $this->_parentTransaction = false; } else { $this->_parentTransaction->hasChildTransaction(true); } } } } return $this->_parentTransaction; } /** * Child transaction(s) getter * * Will attempt to load them first * Can be filtered by types and/or transaction_id * Returns transaction object if transaction_id is specified, otherwise - array * TODO: $recursive is not implemented * * @param array|string $types * @param string $txnId * @param bool $recursive * @return Transaction[] * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getChildTransactions($types = null, $txnId = null, $recursive = false) { $this->_loadChildren(); // grab all transactions if (empty($types) && null === $txnId) { return $this->_children; } elseif ($types && !is_array($types)) { $types = [$types]; } // get a specific transaction if ($txnId) { if (empty($this->_children)) { return null; } $transaction = null; if ($this->_identifiedChildren) { if (isset($this->_identifiedChildren[$txnId])) { $transaction = $this->_identifiedChildren[$txnId]; } } else { foreach ($this->_children as $child) { if ($child->getTxnId() === $txnId) { $transaction = $child; break; } } } // return transaction only if type matches if (!$transaction || $types && !in_array($transaction->getType(), $types, true)) { return null; } return $transaction; } // filter transactions by types $result = []; foreach ($this->_children as $child) { if (in_array($child->getType(), $types, true)) { $result[$child->getId()] = $child; } } return $result; } /** * Close an authorization transaction * * This method can be invoked from any child transaction of the transaction to be closed * Returns the authorization transaction on success. Otherwise false. * $dryRun = true prevents actual closing, it just allows to check whether this operation is possible * * @param bool $shouldSave * @param bool $dryRun * @return bool|\Magento\Sales\Model\Order\Payment\Transaction * @throws \Exception */ public function closeAuthorization($shouldSave = true, $dryRun = false) { try { $this->_verifyThisTransactionExists(); } catch (\Exception $e) { if ($dryRun) { return false; } throw $e; } $authTransaction = false; switch ($this->getTxnType()) { case self::TYPE_VOID: // break intentionally omitted case self::TYPE_CAPTURE: $authTransaction = $this->getParentTransaction(); break; case self::TYPE_AUTH: $authTransaction = $this; break; // case self::TYPE_PAYMENT? default: break; } if ($authTransaction) { if (!$dryRun) { $authTransaction->close($shouldSave); } } return $authTransaction; } /** * Close a capture transaction. Logic is similar to closeAuthorization(), but for a capture transaction * * @param bool $shouldSave * @return bool|\Magento\Sales\Model\Order\Payment\Transaction * @see self::closeAuthorization() */ public function closeCapture($shouldSave = true) { $this->_verifyThisTransactionExists(); $captureTransaction = false; switch ($this->getTxnType()) { case self::TYPE_CAPTURE: $captureTransaction = $this; break; case self::TYPE_REFUND: $captureTransaction = $this->getParentTransaction(); break; default: break; } if ($captureTransaction) { $captureTransaction->close($shouldSave); } return $captureTransaction; } /** * Check whether authorization in current hierarchy can be voided completely * * Basically checks whether the authorization exists and it is not affected by a capture or void * * @return bool */ public function canVoidAuthorizationCompletely() { try { $authTransaction = $this->closeAuthorization('', true); if ($authTransaction->hasChildTransaction() || $this->_children) { return false; } return true; } catch (\Magento\Framework\Exception\LocalizedException $e) { // jam all logical exceptions, fallback to false } return false; } /** * Getter/Setter of whether current transaction has a child transaction * * @param bool $whetherHasChild * @return $this|bool */ public function hasChildTransaction($whetherHasChild = null) { if (null !== $whetherHasChild) { $this->_hasChild = (bool)$whetherHasChild; return $this; } elseif (null === $this->_hasChild) { if ($this->getChildTransactions()) { $this->_hasChild = true; } else { $this->_hasChild = false; } } return $this->_hasChild; } /** * Additional information setter * Updates data inside the 'additional_information' array * Doesn't allow to set arrays * * @param string $key * @param mixed $value * @return $this * @throws \Magento\Framework\Exception\LocalizedException */ public function setAdditionalInformation($key, $value) { if (is_object($value)) { throw new \Magento\Framework\Exception\LocalizedException( __('Payment transactions disallow storing objects.') ); } $info = $this->_getData('additional_information'); if (!$info) { $info = []; } $info[$key] = $value; return $this->setData('additional_information', $info); } /** * Getter for entire additional_information value or one of its element by key * * @param string $key * @return array|null|mixed */ public function getAdditionalInformation($key = null) { $info = $this->_getData('additional_information'); if (!$info) { $info = []; } if ($key) { return $info[$key] ?? null; } return $info; } /** * Unsetter for entire additional_information value or one of its element by key * * @param string $key * @return $this */ public function unsAdditionalInformation($key = null) { if ($key) { $info = $this->_getData('additional_information'); if (is_array($info)) { unset($info[$key]); } } else { $info = []; } return $this->setData('additional_information', $info); } /** * Close this transaction * * @param bool $shouldSave * @return $this * @throws \Magento\Framework\Exception\LocalizedException * @throws \Exception * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function close($shouldSave = true) { if (!$this->_isFailsafe) { $this->_verifyThisTransactionExists(); } if (1 == $this->getIsClosed() && $this->_isFailsafe) { throw new \Magento\Framework\Exception\LocalizedException( __('The transaction "%1" (%2) is already closed.', $this->getTxnId(), $this->getTxnType()) ); } $this->setIsClosed(1); if ($shouldSave) { $this->save(); } if ($this->_transactionsAutoLinking && self::TYPE_AUTH === $this->getTxnType()) { try { $paymentTransaction = $this->getParentTransaction(); if ($paymentTransaction) { $paymentTransaction->close($shouldSave); } } catch (\Exception $e) { if (!$this->_isFailsafe) { throw $e; } } } return $this; } /** * Order ID getter * * Attempts to get ID from set order payment object, if any, or from data by key 'order_id' * * @return int|null */ public function getOrderId() { $orderId = $this->_getData('order_id'); if ($orderId) { return $orderId; } if ($this->getPaymentId()) { $payment = $this->orderPaymentRepository->get($this->getPaymentId()); if ($payment) { $orderId = $payment->getParentId(); } } return $orderId; } /** * Retrieve order instance * * @return \Magento\Sales\Model\Order\Payment */ public function getOrder() { if ($this->_order === null) { $this->setOrder(); } return $this->_order; } /** * Set order instance for transaction depends on transaction behavior * * If $order equals to true, method isn't loading new order instance. * * @param \Magento\Sales\Model\Order|null|boolean $order * @return $this * @throws \Magento\Framework\Exception\LocalizedException */ public function setOrder($order = null) { if (null === $order || $order === true) { if ($this->getOrderId()) { $this->_order = $this->orderRepository->get($this->getOrderId()); } else { $this->_order = false; } } elseif (!$this->getId() || $this->getOrderId() == $order->getId()) { $this->_order = $order; } else { throw new \Magento\Framework\Exception\LocalizedException( __('Set order for existing transactions not allowed') ); } return $this; } /** * Setter/Getter whether transaction is supposed to prevent exceptions on saving * * @param bool|null $setFailsafe * @return $this|bool */ public function isFailsafe($setFailsafe = null) { if (null === $setFailsafe) { return $this->_isFailsafe; } $this->_isFailsafe = (bool)$setFailsafe; return $this; } /** * Verify data required for saving * * @return $this */ public function beforeSave() { if (!$this->getOrderId() && $this->getOrder()) { $this->setOrderId($this->getOrder()->getId()); } if (!$this->getPaymentId() && $this->getOrder() && $this->getOrder()->getPayment()) { $this->setPaymentId($this->getOrder()->getPayment()->getId()); } // set parent id $this->_verifyPaymentObject(); if (!$this->getId()) { $this->setCreatedAt($this->_dateFactory->create()->gmtDate()); } return parent::beforeSave(); } /** * Load child transactions * * @return void * @throws \Magento\Framework\Exception\LocalizedException * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ protected function _loadChildren() { if (null !== $this->_children) { return; } // make sure minimum required data is set $this->_verifyThisTransactionExists(); if (!$this->getPaymentId()) { throw new \Magento\Framework\Exception\LocalizedException(__('At minimum, you need to set a payment ID.')); } $this->setOrder(true); $orderFilter = $this->getOrder(); // Try to get order instance for filter if (!$orderFilter) { $orderFilter = $this->getOrderId(); } // prepare children collection $children = $this->getResourceCollection()->setOrderFilter( $orderFilter )->addPaymentIdFilter( $this->getPaymentId() )->addParentIdFilter( $this->getId() ); // set basic children array and attempt to map them per txn_id, if all of them have txn_id $this->_children = []; $this->_identifiedChildren = []; foreach ($children as $child) { if ($this->getPaymentId()) { $child->setOrderId($this->getOrderId())->setPaymentId($this->getPaymentId()); } $this->_children[$child->getId()] = $child; if (false !== $this->_identifiedChildren) { $childTxnId = $child->getTxnId(); if (!$childTxnId || '0' == $childTxnId) { $this->_identifiedChildren = false; } else { $this->_identifiedChildren[$child->getTxnId()] = $child; } } } if (false === $this->_identifiedChildren) { $this->_identifiedChildren = []; } } /** * Check whether this transaction is voided * * @return bool */ protected function _isVoided() { $this->_verifyThisTransactionExists(); return self::TYPE_AUTH === $this->getTxnType() && (bool)count($this->getChildTransactions(self::TYPE_VOID)); } /** * Check whether this transaction is voided * * @return bool */ public function isVoided() { return $this->_isVoided(); } /** * Retrieve transaction types * * @return array */ public function getTransactionTypes() { return [ \Magento\Sales\Model\Order\Payment\Transaction::TYPE_ORDER => __('Order'), \Magento\Sales\Model\Order\Payment\Transaction::TYPE_AUTH => __('Authorization'), \Magento\Sales\Model\Order\Payment\Transaction::TYPE_CAPTURE => __('Capture'), \Magento\Sales\Model\Order\Payment\Transaction::TYPE_VOID => __('Void'), \Magento\Sales\Model\Order\Payment\Transaction::TYPE_REFUND => __('Refund') ]; } /** * Retrieve order website id * * @return int */ public function getOrderWebsiteId() { if ($this->_orderWebsiteId === null) { $this->_orderWebsiteId = (int)$this->getResource()->getOrderWebsiteId($this->getOrderId()); } return $this->_orderWebsiteId; } /** * Check whether specified or set transaction type is supported * * @param string $txnType * @return void * @throws \Magento\Framework\Exception\LocalizedException */ protected function _verifyTxnType($txnType = null) { if (null === $txnType) { $txnType = $this->getTxnType(); } switch ($txnType) { case self::TYPE_PAYMENT: case self::TYPE_ORDER: case self::TYPE_AUTH: case self::TYPE_CAPTURE: case self::TYPE_VOID: case self::TYPE_REFUND: break; default: throw new \Magento\Framework\Exception\LocalizedException( __('We found an unsupported transaction type "%1".', $txnType) ); } } /** * Check whether the payment object is set and it has order object or there is an order_id is set * $dryRun allows to not throw exception * * @param bool $dryRun * @return void * @throws \Magento\Framework\Exception\LocalizedException */ protected function _verifyPaymentObject($dryRun = false) { if (!$this->getPaymentId() || !$this->getOrderId()) { if (!$dryRun) { throw new \Magento\Framework\Exception\LocalizedException( __('Please set a proper payment and order id.') ); } } } /** * Check whether specified transaction ID is valid * * @param string $txnId * @return void * @throws \Magento\Framework\Exception\LocalizedException */ protected function _verifyTxnId($txnId) { if (null !== $txnId && 0 == strlen($txnId)) { throw new \Magento\Framework\Exception\LocalizedException(__('Please enter a Transaction ID.')); } } /** * Make sure this object is a valid transaction * * @return void * @throws \Magento\Framework\Exception\LocalizedException */ protected function _verifyThisTransactionExists() { if (!$this->getId()) { throw new \Magento\Framework\Exception\LocalizedException( __('You can\'t do this without a transaction object.') ); } $this->_verifyTxnType(); } //@codeCoverageIgnoreStart /** * Returns transaction_id * * @return int */ public function getTransactionId() { return $this->getData(TransactionInterface::TRANSACTION_ID); } /** * @inheritdoc */ public function setTransactionId($id) { return $this->setData(TransactionInterface::TRANSACTION_ID, $id); } /** * Returns method * * @return string */ public function getMethod() { return $this->getData(TransactionInterface::METHOD); } /** * Returns increment_id * * @return string */ public function getIncrementId() { return $this->getData(TransactionInterface::INCREMENT_ID); } /** * Returns parent_id * * @return int|null */ public function getParentId() { return $this->getData(TransactionInterface::PARENT_ID); } /** * Returns payment_id * * @return int */ public function getPaymentId() { return $this->getData(TransactionInterface::PAYMENT_ID); } /** * Returns txn_id * * @return string */ public function getTxnId() { return $this->getData(TransactionInterface::TXN_ID); } /** * Get HTML format for transaction id * * @return string */ public function getHtmlTxnId() { $this->_eventManager->dispatch($this->_eventPrefix . '_html_txn_id', $this->_getEventData()); return $this->_data['html_txn_id'] ?? $this->getTxnId(); } /** * Returns parent_txn_id * * @return string */ public function getParentTxnId() { return $this->getData(TransactionInterface::PARENT_TXN_ID); } /** * Returns txn_type * * @return string */ public function getTxnType() { return $this->getData(TransactionInterface::TXN_TYPE); } /** * Returns is_closed * * @return int */ public function getIsClosed() { return $this->getData(TransactionInterface::IS_CLOSED); } /** * Gets the created-at timestamp for the transaction. * * @return string */ public function getCreatedAt() { return $this->getData(TransactionInterface::CREATED_AT); } /** * @inheritdoc */ public function setCreatedAt($createdAt) { return $this->setData(TransactionInterface::CREATED_AT, $createdAt); } /** * @inheritdoc */ public function setParentId($id) { return $this->setData(TransactionInterface::PARENT_ID, $id); } /** * @inheritdoc */ public function setOrderId($id) { return $this->setData(TransactionInterface::ORDER_ID, $id); } /** * @inheritdoc */ public function setPaymentId($id) { return $this->setData(TransactionInterface::PAYMENT_ID, $id); } /** * @inheritdoc */ public function setIsClosed($isClosed) { return $this->setData(TransactionInterface::IS_CLOSED, $isClosed); } /** * @inheritdoc * * @return \Magento\Sales\Api\Data\TransactionExtensionInterface|null */ public function getExtensionAttributes() { return $this->_getExtensionAttributes(); } /** * @inheritdoc */ public function setExtensionAttributes(\Magento\Sales\Api\Data\TransactionExtensionInterface $extensionAttributes) { return $this->_setExtensionAttributes($extensionAttributes); } //@codeCoverageIgnoreEnd }