/** * Process payment refund notification * Updates transactions hierarchy, if required * Prevents transaction double processing * Updates payment totals, updates order status and adds proper comments * TODO: potentially a full capture can be refunded. In this case if there was only one invoice for that transaction * then we should create a creditmemo from invoice and also refund it offline * TODO: implement logic of chargebacks reimbursements (via negative amount) * * @param float $amount * @return $this * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function registerRefundNotification($amount) { $notificationAmount = $amount; $this->setTransactionId($this->transactionManager->generateTransactionId($this, Transaction::TYPE_REFUND, $this->transactionRepository->getByTransactionId($this->getParentTransactionId(), $this->getId(), $this->getOrder()->getId()))); if ($this->checkIfTransactionExists()) { return $this; } $order = $this->getOrder(); $invoice = $this->_getInvoiceForTransactionId($this->getParentTransactionId()); //choose where we get totals $salesEntity = $invoice ?: $order; $baseGrandTotal = $salesEntity->getBaseGrandTotal(); $amountRefundLeft = $baseGrandTotal - $salesEntity->getBaseTotalRefunded(); if ($amountRefundLeft < $amount) { $amount = $amountRefundLeft; } if ($amount != $baseGrandTotal) { $order->addStatusHistoryComment(__('IPN "Refunded". Refund issued by merchant. Registered notification about refunded amount of %1. Transaction ID: "%2". Credit Memo has not been created. Please create offline Credit Memo.', $this->formatPrice($notificationAmount), $this->getTransactionId()), false); return $this; } $creditmemo = $this->prepareCreditMemo($amount, $baseGrandTotal, $invoice); $creditmemo->setPaymentRefundDisallowed(true)->setAutomaticallyCreated(true)->register()->addComment(__('The credit memo has been created automatically.')); $creditmemo->save(); $this->_updateTotals(['amount_refunded' => $creditmemo->getGrandTotal(), 'base_amount_refunded_online' => $amount]); $this->setCreatedCreditmemo($creditmemo); // update transactions and order state $transaction = $this->addTransaction(Transaction::TYPE_REFUND, $creditmemo); $message = $this->prependMessage(__('Registered notification about refunded amount of %1.', $this->formatPrice($amount))); $message = $this->_appendTransactionToMessage($transaction, $message); $this->setOrderStateProcessing($message); return $this; }
/** * Links transaction with parent transaction * * @param TransactionInterface $transaction * @return TransactionInterface */ protected function linkWithParentTransaction(TransactionInterface $transaction) { $parentTransactionId = $this->payment->getParentTransactionId(); if ($parentTransactionId) { $transaction->setParentTxnId($parentTransactionId); if ($this->payment->getShouldCloseParentTransaction()) { $parentTransaction = $this->transactionRepository->getByTransactionId($parentTransactionId, $this->payment->getid(), $this->order->getId()); if ($parentTransaction) { if (!$parentTransaction->getIsClosed()) { $parentTransaction->isFailsafe($this->failSafe)->close(false); } $this->order->addRelatedObject($parentTransaction); } } } return $transaction; }
/** * Checks if transaction exists by txt id * * @param string $transactionId * @param int $paymentId * @param int $orderId * @return bool */ public function isTransactionExists($transactionId, $paymentId, $orderId) { return $transactionId && $this->transactionRepository->getByTransactionId($transactionId, $paymentId, $orderId); }
/** * Fetch transaction details info * * Update transaction info if there is one placing transaction only * * @param \Magento\Payment\Model\InfoInterface $payment * @param string $transactionId * @return array */ public function fetchTransactionInfo(\Magento\Payment\Model\InfoInterface $payment, $transactionId) { $transaction = $this->transactionRepository->getByTransactionId($transactionId, $payment->getId(), $payment->getOrder()->getId()); $response = $this->getTransactionResponse($transactionId); if ($response->getXResponseCode() == self::RESPONSE_CODE_APPROVED) { if ($response->getTransactionStatus() == 'voided') { $payment->setIsTransactionDenied(true); $payment->setIsTransactionClosed(true); $transaction->close(); } else { $transaction->setAdditionalInformation(self::TRANSACTION_FRAUD_STATE_KEY, false); $payment->setIsTransactionApproved(true); } } elseif ($response->getXResponseReasonCode() == self::RESPONSE_REASON_CODE_PENDING_REVIEW_DECLINED) { $payment->setIsTransactionDenied(true); } $this->addStatusCommentOnUpdate($payment, $response, $transactionId); return []; }