/** * 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; }
/** * Check if capture transaction already exists * * @param OrderPaymentInterface $payment * @return bool */ private function isExistsCaptureTransaction(OrderPaymentInterface $payment) { $filters[] = $this->filterBuilder->setField('payment_id')->setValue($payment->getId())->create(); $filters[] = $this->filterBuilder->setField('txn_type')->setValue(TransactionInterface::TYPE_CAPTURE)->create(); $searchCriteria = $this->searchCriteriaBuilder->addFilters($filters)->create(); $count = $this->transactionRepository->getList($searchCriteria)->getTotalCount(); return (bool) $count; }
/** * @param string $payuplOrderId * @param string $status * @param bool $close * @throws LocalizedException */ public function updateStatus($payuplOrderId, $status, $close = false) { /** * @var $transaction \Magento\Sales\Model\Order\Payment\Transaction */ $id = $this->transactionResource->getIdByPayuplOrderId($payuplOrderId); if (!$id) { throw new LocalizedException(new Phrase('Transaction ' . $payuplOrderId . ' not found.')); } $transaction = $this->transactionRepository->get($id); if ($close) { $transaction->setIsClosed(1); } $rawDetailsInfo = $transaction->getAdditionalInformation(\Magento\Sales\Model\Order\Payment\Transaction::RAW_DETAILS); $rawDetailsInfo['status'] = $status; $transaction->setAdditionalInformation(\Magento\Sales\Model\Order\Payment\Transaction::RAW_DETAILS, $rawDetailsInfo)->save(); }
/** * Build search criteria */ private function buildSearchCriteria() { $this->filterBuilder->expects(static::exactly(2))->method('setField')->willReturnSelf(); $this->filterBuilder->expects(static::exactly(2))->method('setValue')->willReturnSelf(); $searchCriteria = new SearchCriteria(); $this->searchCriteriaBuilder->expects(static::once())->method('addFilters')->willReturnSelf(); $this->searchCriteriaBuilder->expects(static::once())->method('create')->willReturn($searchCriteria); $this->transactionRepository->expects(static::once())->method('getList')->with($searchCriteria)->willReturnSelf(); }
/** * 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; }
/** * @param int $size * @param string $command * @covers \Magento\BraintreeTwo\Gateway\Command\CaptureStrategyCommand::execute * @dataProvider captureDataProvider */ public function testCaptureExecute($size, $command) { $paymentData = $this->getPaymentDataObjectMock(); $subject['payment'] = $paymentData; $this->subjectReaderMock->expects(self::once())->method('readPayment')->with($subject)->willReturn($paymentData); $this->payment->expects(static::once())->method('getAuthorizationTransaction')->willReturn(true); $this->payment->expects(static::once())->method('getId')->willReturn(1); $this->filterBuilder->expects(static::exactly(2))->method('setField')->willReturnSelf(); $this->filterBuilder->expects(static::exactly(2))->method('setValue')->willReturnSelf(); $searchCriteria = new SearchCriteria(); $this->searchCriteriaBuilder->expects(static::once())->method('addFilters')->willReturnSelf(); $this->searchCriteriaBuilder->expects(static::once())->method('create')->willReturn($searchCriteria); $this->transactionRepository->expects(static::once())->method('getList')->with($searchCriteria)->willReturnSelf(); $this->transactionRepository->expects(static::once())->method('getTotalCount')->willReturn($size); $this->commandPool->expects(static::once())->method('get')->with($command)->willReturn($this->command); $this->strategyCommand->execute($subject); }
/** * Get transaction with type order * * @param OrderPaymentInterface $payment * * @return false|\Magento\Sales\Api\Data\TransactionInterface */ protected function getOrderTransaction($payment) { return $this->transactionRepository->getByTransactionType(Transaction::TYPE_ORDER, $payment->getId(), $payment->getOrder()->getId()); }
/** * Return option array * * @return array */ public function toOptionArray() { return $this->transactionRepository->create()->getTransactionTypes(); }
/** * 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 []; }