diff --git a/Api/PushProcessorInterface.php b/Api/PushProcessorInterface.php new file mode 100644 index 000000000..5b7b0fcdc --- /dev/null +++ b/Api/PushProcessorInterface.php @@ -0,0 +1,12 @@ +searchCriteriaBuilder = $searchCriteriaBuilder; $this->transactionRepository = $transactionRepository; @@ -151,7 +112,6 @@ public function __construct( * Redirect Process Payconiq * * @return ResponseInterface|void - * @throws LocalizedException * @throws Exception */ public function execute() @@ -162,7 +122,7 @@ public function execute() } $transaction = $this->getTransaction(); - $this->order = $transaction->getOrder(); + $this->order = $transaction->getOrder()->getOrder(); if ($this->customerSession->getCustomerId() == $this->order->getCustomerId()) { $this->logger->addError('Customer is different then the customer that start payconiq process request.'); @@ -173,7 +133,7 @@ public function execute() 'checkout', [ '_fragment' => 'payment', - '_query' => ['bk_e' => 1] + '_query' => ['bk_e' => 1] ] ); } @@ -181,9 +141,7 @@ public function execute() // @codingStandardsIgnoreStart try { - $this->handleFailed( - $this->helper->getStatusCode('BUCKAROO_MAGENTO2_STATUSCODE_CANCELLED_BY_USER') - ); + $this->handleFailed(BuckarooStatusCode::CANCELLED_BY_USER); } catch (\Exception $exception) { // handle failed exception } @@ -212,7 +170,7 @@ protected function getTransactionKey() /** * Get transaction object * - * @return TransactionInterface|Transaction + * @return TransactionInterface|Transaction|null * @throws Exception */ protected function getTransaction() @@ -239,7 +197,7 @@ protected function getTransaction() * @return TransactionSearchResultInterface * @throws Exception */ - protected function getList() + protected function getList(): TransactionSearchResultInterface { $transactionKey = $this->getTransactionKey(); diff --git a/Controller/Redirect/IdinProcess.php b/Controller/Redirect/IdinProcess.php new file mode 100644 index 000000000..e329e6dc4 --- /dev/null +++ b/Controller/Redirect/IdinProcess.php @@ -0,0 +1,168 @@ +customerResourceFactory = $customerFactory; + } + + /** + * @return ResponseInterface|void + * @throws \Buckaroo\Magento2\Exception + */ + public function execute(): ResponseInterface + { + // Initialize the order, quote, payment + if ($this->redirectRequest->hasPostData('primary_service', 'IDIN')) { + if ($this->setCustomerIDIN()) { + $this->addSuccessMessage(__('Your iDIN verified succesfully!')); + } else { + $this->addErrorMessage( + __( + 'Unfortunately iDIN not verified!' + ) + ); + } + + return $this->redirectToCheckout(); + } + } + + /** + * Set consumer bin IDIN on customer + * + * @return bool + */ + private function setCustomerIDIN(): bool + { + if (!empty($this->redirectRequest->getServiceIdinConsumerbin()) + && !empty($this->redirectRequest->getServiceIdinIseighteenorolder()) + && $this->redirectRequest->getServiceIdinIseighteenorolder() == 'True' + ) { + $this->checkoutSession->setCustomerIDIN($this->redirectRequest->getServiceIdinConsumerbin()); + $this->checkoutSession->setCustomerIDINIsEighteenOrOlder(true); + $idinCid = $this->redirectRequest->getAdditionalInformation('idin_cid'); + if (!empty($idinCid)) { + try { + /** @var Customer $customerNew */ + $customerNew = $this->customerRepository->getById((int)$idinCid); + } catch (\Exception $e) { + $this->addErrorMessage(__('Unfortunately customer was not find by IDIN id: "%1"!', $idinCid)); + $this->logger->addError(__METHOD__ . ' | ' . $e->getMessage()); + return false; + } + $customerData = $customerNew->getDataModel(); + $customerData->setCustomAttribute('buckaroo_idin', $this->redirectRequest->getServiceIdinConsumerbin()); + $customerData->setCustomAttribute('buckaroo_idin_iseighteenorolder', 1); + $customerNew->updateData($customerData); + + $customerResource = $this->customerResourceFactory->create(); + $customerResource->saveAttribute($customerNew, 'buckaroo_idin'); + $customerResource->saveAttribute($customerNew, 'buckaroo_idin_iseighteenorolder'); + } + return true; + } + return false; + } + + /** + * Create redirect response + * + * @return ResponseInterface + */ + protected function redirectToCheckout(): ResponseInterface + { + $this->logger->addDebug('start redirectToCheckout'); + try { + $this->checkoutSession->restoreQuote(); + + } catch (\Exception $e) { + $this->logger->addError('Could not restore the quote.'); + } + + return $this->handleProcessedResponse('checkout', ['_query' => ['bk_e' => 1]]); + } +} \ No newline at end of file diff --git a/Controller/Redirect/Process.php b/Controller/Redirect/Process.php index e901554e8..f5e4f8f4c 100644 --- a/Controller/Redirect/Process.php +++ b/Controller/Redirect/Process.php @@ -17,209 +17,157 @@ * @copyright Copyright (c) Buckaroo B.V. * @license https://tldrlegal.com/license/mit-license */ +declare(strict_types=1); namespace Buckaroo\Magento2\Controller\Redirect; use Buckaroo\Magento2\Api\PushRequestInterface; -use Buckaroo\Magento2\Exception; -use Buckaroo\Magento2\Helper\Data; use Buckaroo\Magento2\Logging\Log; -use Buckaroo\Magento2\Model\ConfigProvider\Factory; +use Buckaroo\Magento2\Model\BuckarooStatusCode; +use Buckaroo\Magento2\Model\ConfigProvider\Account as AccountConfig; use Buckaroo\Magento2\Model\Method\BuckarooAdapter; use Buckaroo\Magento2\Model\OrderStatusFactory; use Buckaroo\Magento2\Model\RequestPush\RequestPushFactory; use Buckaroo\Magento2\Model\Service\Order as OrderService; +use Buckaroo\Magento2\Service\Push\OrderRequestService; use Buckaroo\Magento2\Service\Sales\Quote\Recreate; -use Magento\Checkout\Model\Cart; -use Magento\Checkout\Model\ConfigProviderInterface; +use Magento\Checkout\Model\Session as CheckoutSession; use Magento\Customer\Api\CustomerRepositoryInterface; -use Magento\Customer\Model\Customer; use Magento\Customer\Model\ResourceModel\CustomerFactory; use Magento\Customer\Model\Session as CustomerSession; -use Magento\Checkout\Model\Session as CheckoutSession; -use Magento\Customer\Model\SessionFactory; use Magento\Framework\App\Action\Action; use Magento\Framework\App\Action\Context; use Magento\Framework\App\Request\Http as Http; use Magento\Framework\App\ResponseInterface; use Magento\Framework\Event\ManagerInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Phrase; use Magento\Quote\Model\Quote; +use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\Data\OrderPaymentInterface; -use Magento\Sales\Api\Data\TransactionInterface; use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Email\Sender\OrderSender; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - * @SuppressWarnings(PHPMD.TooManyFields) */ class Process extends Action { - /** - * @var CustomerSession - */ - public $customerSession; - - /** - * @var array - */ - protected $response; - + private const GENERAL_ERROR_MESSAGE = 'Unfortunately an error occurred while processing your payment. ' . + 'Please try again. If this error persists, please choose a different payment method.'; /** * @var Order $order */ - protected $order; + protected Order $order; /** * @var Quote $quote */ - protected $quote; - - /** - * @var Data $helper - */ - protected $helper; + protected Quote $quote; /** - * @var Cart + * @var OrderPaymentInterface|null */ - protected $cart; + protected ?OrderPaymentInterface $payment; /** - * @var ConfigProviderInterface + * @var AccountConfig */ - protected $accountConfig; + protected AccountConfig $accountConfig; /** - * @var OrderSender + * @var OrderRequestService */ - protected $orderSender; + protected OrderRequestService $orderRequestService; /** * @var OrderStatusFactory */ - protected $orderStatusFactory; + protected OrderStatusFactory $orderStatusFactory; /** * @var Log */ - protected $logger; + protected Log $logger; /** * @var CheckoutSession */ - protected $checkoutSession; - - /** - * @var CustomerRepositoryInterface - */ - protected $customerRepository; + protected CheckoutSession $checkoutSession; /** - * @var SessionFactory - */ - protected $_sessionFactory; - - /** - * @var Customer + * @var CustomerSession */ - protected $customerModel; + protected CustomerSession $customerSession; /** - * @var CustomerFactory + * @var CustomerRepositoryInterface */ - protected $customerResourceFactory; + protected CustomerRepositoryInterface $customerRepository; /** * @var OrderService */ - protected $orderService; - - /** - * @var TransactionInterface - */ - private $transaction; + protected OrderService $orderService; /** * @var ManagerInterface */ - protected $eventManager; + protected ManagerInterface $eventManager; /** * @var Recreate */ - private $quoteRecreate; + protected Recreate $quoteRecreate; /** * @var PushRequestInterface */ - private PushRequestInterface $pushRequst; + protected PushRequestInterface $redirectRequest; /** * @param Context $context - * @param Data $helper - * @param Cart $cart - * @param Order $order - * @param Quote $quote - * @param TransactionInterface $transaction * @param Log $logger - * @param Factory $configProviderFactory - * @param OrderSender $orderSender + * @param Quote $quote + * @param AccountConfig $accountConfig + * @param OrderRequestService $orderRequestService * @param OrderStatusFactory $orderStatusFactory * @param CheckoutSession $checkoutSession * @param CustomerSession $customerSession * @param CustomerRepositoryInterface $customerRepository - * @param SessionFactory $sessionFactory - * @param Customer $customerModel - * @param CustomerFactory $customerFactory * @param OrderService $orderService * @param ManagerInterface $eventManager * @param Recreate $quoteRecreate * @param RequestPushFactory $requestPushFactory - * @throws Exception * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( Context $context, - Data $helper, - Cart $cart, - Order $order, - Quote $quote, - TransactionInterface $transaction, Log $logger, - Factory $configProviderFactory, - OrderSender $orderSender, + Quote $quote, + AccountConfig $accountConfig, + OrderRequestService $orderRequestService, OrderStatusFactory $orderStatusFactory, CheckoutSession $checkoutSession, CustomerSession $customerSession, CustomerRepositoryInterface $customerRepository, - SessionFactory $sessionFactory, - Customer $customerModel, - CustomerFactory $customerFactory, OrderService $orderService, ManagerInterface $eventManager, Recreate $quoteRecreate, RequestPushFactory $requestPushFactory ) { parent::__construct($context); - $this->helper = $helper; - $this->cart = $cart; - $this->order = $order; - $this->quote = $quote; - $this->transaction = $transaction; $this->logger = $logger; - $this->orderSender = $orderSender; + $this->orderRequestService = $orderRequestService; $this->orderStatusFactory = $orderStatusFactory; $this->checkoutSession = $checkoutSession; $this->customerSession = $customerSession; $this->customerRepository = $customerRepository; - $this->_sessionFactory = $sessionFactory; - $this->customerModel = $customerModel; - $this->customerResourceFactory = $customerFactory; - $this->accountConfig = $configProviderFactory->get('account'); + $this->accountConfig = $accountConfig; $this->orderService = $orderService; $this->eventManager = $eventManager; $this->quoteRecreate = $quoteRecreate; + $this->quote = $quote; // @codingStandardsIgnoreStart if (interface_exists("\Magento\Framework\App\CsrfAwareActionInterface")) { @@ -229,199 +177,54 @@ public function __construct( $request->getHeaders()->addHeaderLine('X_REQUESTED_WITH', 'XMLHttpRequest'); } } - $this->pushRequst = $requestPushFactory->create(); // @codingStandardsIgnoreEnd + $this->redirectRequest = $requestPushFactory->create(); } /** * Process action * - * @return ResponseInterface + * @return ResponseInterface|void * @throws \Exception - * - * - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) */ public function execute() { - $this->logger->addDebug(__METHOD__ . '|' . var_export($this->pushRequst->getOriginalRequest(), true)); + $this->logger->addDebug(__METHOD__ . '|' . var_export($this->redirectRequest->getOriginalRequest(), true)); - /** - * Check if there is a valid response. If not, redirect to home. - */ - if (count($this->pushRequst->getData()) === 0 || empty($this->pushRequst->getStatusCode())) { + if (count($this->redirectRequest->getData()) === 0 || empty($this->redirectRequest->getStatusCode())) { return $this->handleProcessedResponse('/'); } - if ($this->pushRequst->hasPostData('primary_service', 'IDIN')) { - if ($this->setCustomerIDIN()) { - $this->addSuccessMessage(__('Your iDIN verified succesfully!')); - } else { - $this->addErrorMessage( - __( - 'Unfortunately iDIN not verified!' - ) - ); - } - - return $this->redirectToCheckout(); - } - - $statusCode = (int)$this->pushRequst->getStatusCode(); - - $this->loadOrder(); - $this->helper->setRestoreQuoteLastOrder(false); + $this->order = $this->orderRequestService->getOrderByRequest($this->redirectRequest); + $statusCode = (int)$this->redirectRequest->getStatusCode(); if (!$this->order->getId()) { - $statusCode = $this->helper->getStatusCode('BUCKAROO_MAGENTO2_ORDER_FAILED'); + $statusCode = BuckarooStatusCode::ORDER_FAILED; } else { $this->quote->load($this->order->getQuoteId()); } - $payment = $this->order->getPayment(); - - if ($payment) { - $this->setPaymentOutOfTransit($payment); + $this->payment = $this->order->getPayment(); + if ($this->payment) { + $this->setPaymentOutOfTransit($this->payment); } - if (!method_exists($payment->getMethodInstance(), 'canProcessPostData')) { - return $this->handleProcessedResponse('/'); - } + $this->checkoutSession->setRestoreQuoteLastOrder(false); - if (!$payment->getMethodInstance()->canProcessPostData($payment, $this->pushRequst)) { + if ($this->skipWaitingOnConsumerForProcessingOrder()) { return $this->handleProcessedResponse('/'); } $this->logger->addDebug(__METHOD__ . '|2|' . var_export($statusCode, true)); - if (($payment->getMethodInstance()->getCode() == 'buckaroo_magento2_paypal') - && ($statusCode == $this->helper->getStatusCode('BUCKAROO_MAGENTO2_STATUSCODE_PENDING_PROCESSING')) + if (($this->payment->getMethodInstance()->getCode() == 'buckaroo_magento2_paypal') + && ($statusCode == BuckarooStatusCode::PENDING_PROCESSING) ) { - $statusCode = $this->helper->getStatusCode('BUCKAROO_MAGENTO2_STATUSCODE_CANCELLED_BY_USER'); + $statusCode = BuckarooStatusCode::CANCELLED_BY_USER; $this->logger->addDebug(__METHOD__ . '|22|' . var_export($statusCode, true)); } - switch ($statusCode) { - case $this->helper->getStatusCode('BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS'): - case $this->helper->getStatusCode('BUCKAROO_MAGENTO2_STATUSCODE_PENDING_PROCESSING'): - $debugInfo = [ - $this->order->getStatus(), - $this->orderStatusFactory->get( - $this->helper->getStatusCode('BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS'), - $this->order - ), - ]; - $this->logger->addDebug(__METHOD__ . '|3|' . var_export($debugInfo, true)); - - if ($this->order->canInvoice()) { - $this->logger->addDebug(__METHOD__ . '|31|'); - if ($statusCode == $this->helper->getStatusCode('BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS')) { - //do nothing - push will change a status - $this->logger->addDebug(__METHOD__ . '|32|'); - } else { - $this->logger->addDebug(__METHOD__ . '|33|'); - // Set the 'Pending payment status' here - $pendingStatus = $this->orderStatusFactory->get( - $this->helper->getStatusCode('BUCKAROO_MAGENTO2_STATUSCODE_PENDING_PROCESSING'), - $this->order - ); - if ($pendingStatus) { - $this->logger->addDebug(__METHOD__ . '|34|' . var_export($pendingStatus, true)); - $this->order->setStatus($pendingStatus); - $this->order->save(); - } - } - } - - $payment->getMethodInstance()->processCustomPostData($payment, $this->pushRequst->getData()); - - /** @var \Magento\Payment\Model\MethodInterface $paymentMethod */ - $paymentMethod = $this->order->getPayment()->getMethodInstance(); - $store = $this->order->getStore(); - - // Send order confirmation mail if we're supposed to - /** - * @noinspection PhpUndefinedMethodInspection - */ - if (!$this->order->getEmailSent() - && ( - $this->accountConfig->getOrderConfirmationEmail($store) === "1" - || $paymentMethod->getConfigData('order_email', $store) === "1" - ) - ) { - $isKlarnaKpReserve = ($this->pushRequst->hasPostData('primary_service', 'KlarnaKp') - && $this->pushRequst->hasAdditionalInformation('service_action_from_magento', 'reserve') - && !empty($this->pushRequst->getServiceKlarnakpReservationnumber())); - - if (!($this->pushRequst->hasAdditionalInformation('initiated_by_magento', 1) - && $isKlarnaKpReserve) - ) { - if ($statusCode == $this->helper->getStatusCode('BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS')) { - $this->logger->addDebug(__METHOD__ . '|sendemail|'); - $this->orderSender->send($this->order, true); - } - } - } - - $pendingCode = $this->helper->getStatusCode('BUCKAROO_MAGENTO2_STATUSCODE_PENDING_PROCESSING'); - if (($statusCode == $pendingCode) - && !$this->pushRequst->hasPostData('payment_method', 'sofortueberweisung') - ) { - $this->addErrorMessage( - __( - 'Unfortunately an error occurred while processing your payment. Please try again. If this' . - ' error persists, please choose a different payment method.' - ) - ); - $this->logger->addDebug(__METHOD__ . '|5|'); - - $this->removeCoupon(); - $this->removeAmastyGiftcardOnFailed(); - - return $this->handleProcessedResponse('/'); - } - - $this->logger->addDebug(__METHOD__ . '|51|' . var_export([ - $this->checkoutSession->getLastSuccessQuoteId(), - $this->checkoutSession->getLastQuoteId(), - $this->checkoutSession->getLastOrderId(), - $this->checkoutSession->getLastRealOrderId(), - $this->order->getQuoteId(), - $this->order->getId(), - $this->order->getIncrementId(), - ], true)); - - if (!$this->checkoutSession->getLastSuccessQuoteId() && $this->order->getQuoteId()) { - $this->logger->addDebug(__METHOD__ . '|52|'); - $this->checkoutSession->setLastSuccessQuoteId($this->order->getQuoteId()); - } - if (!$this->checkoutSession->getLastQuoteId() && $this->order->getQuoteId()) { - $this->logger->addDebug(__METHOD__ . '|53|'); - $this->checkoutSession->setLastQuoteId($this->order->getQuoteId()); - } - if (!$this->checkoutSession->getLastOrderId() && $this->order->getId()) { - $this->logger->addDebug(__METHOD__ . '|54|'); - $this->checkoutSession->setLastOrderId($this->order->getId()); - } - if (!$this->checkoutSession->getLastRealOrderId() && $this->order->getIncrementId()) { - $this->logger->addDebug(__METHOD__ . '|55|'); - $this->checkoutSession->setLastRealOrderId($this->order->getIncrementId()); - } - $this->logger->addDebug(__METHOD__ . '|6|'); - // Redirect to success page - return $this->redirectSuccess(); - case $this->helper->getStatusCode('BUCKAROO_MAGENTO2_ORDER_FAILED'): - case $this->helper->getStatusCode('BUCKAROO_MAGENTO2_STATUSCODE_FAILED'): - case $this->helper->getStatusCode('BUCKAROO_MAGENTO2_STATUSCODE_REJECTED'): - case $this->helper->getStatusCode('BUCKAROO_MAGENTO2_STATUSCODE_CANCELLED_BY_USER'): - return $this->handleFailed($statusCode); - //no default - } - - $this->logger->addDebug(__METHOD__ . '|9|'); - return $this->_response; + return $this->processRedirectByStatus($statusCode); } /** @@ -432,170 +235,298 @@ public function execute() * * @return ResponseInterface */ - public function handleProcessedResponse($path, $arguments = []) + public function handleProcessedResponse(string $path, array $arguments = []): ResponseInterface { $this->logger->addDebug(__METHOD__ . '|15|'); return $this->_redirect($path, $arguments); } /** - * Set consumer bin IDIN on customer + * Set flag if user is on the payment provider page + * + * @param OrderPaymentInterface $payment + * @return void + * @throws \Exception + */ + protected function setPaymentOutOfTransit(OrderPaymentInterface $payment): void + { + $payment->setAdditionalInformation(BuckarooAdapter::BUCKAROO_PAYMENT_IN_TRANSIT, false)->save(); + } + + /** + * Skip process redirect for Processing Order when the status of the request is WaitingOnConsumer * * @return bool */ - private function setCustomerIDIN() + public function skipWaitingOnConsumerForProcessingOrder(): bool { - if (!empty($this->pushRequst->getServiceIdinConsumerbin()) - && !empty($this->pushRequst->getServiceIdinIseighteenorolder()) - && $this->pushRequst->getServiceIdinIseighteenorolder() == 'True' - ) { - $this->checkoutSession->setCustomerIDIN($this->pushRequst->getServiceIdinConsumerbin()); - $this->checkoutSession->setCustomerIDINIsEighteenOrOlder(true); - if (!empty($this->pushRequst->getAdditionalInformation('idin_cid'))) { - $customerNew = $this->customerModel->load((int)$this->pushRequst->getAdditionalInformation('idin_cid')); - $customerData = $customerNew->getDataModel(); - $customerData->setCustomAttribute('buckaroo_idin', $this->pushRequst->getServiceIdinConsumerbin()); - $customerData->setCustomAttribute('buckaroo_idin_iseighteenorolder', 1); - $customerNew->updateData($customerData); - $customerResource = $this->customerResourceFactory->create(); - $customerResource->saveAttribute($customerNew, 'buckaroo_idin'); - $customerResource->saveAttribute($customerNew, 'buckaroo_idin_iseighteenorolder'); + if (in_array($this->payment->getMethod(), + [ + 'buckaroo_magento2_creditcards', + 'buckaroo_magento2_paylink', + 'buckaroo_magento2_payperemail', + 'buckaroo_magento2_transfer' + ])) { + + if ($this->payment->getAdditionalInformation(BuckarooAdapter::BUCKAROO_ORIGINAL_TRANSACTION_KEY_KEY) + != $this->redirectRequest->getTransactions()) { + return true; + } + + $orderState = $this->order->getState(); + if ($orderState == Order::STATE_PROCESSING + && $this->redirectRequest->getStatusCode() == BuckarooStatusCode::WAITING_ON_CONSUMER) { + return true; } - return true; } + return false; } /** - * Add success message to be displayed to the user - * - * @param string $message + * Processes a redirect based on the given status code. * - * @return void + * @param int $statusCode + * @return ResponseInterface + * @throws LocalizedException + * @throws NoSuchEntityException */ - public function addSuccessMessage(string $message) + private function processRedirectByStatus(int $statusCode): ResponseInterface { - $this->messageManager->addSuccessMessage($message); + if ($statusCode == BuckarooStatusCode::SUCCESS) { + return $this->processSucceededRedirect($statusCode); + } elseif ($statusCode == BuckarooStatusCode::PENDING_PROCESSING) { + return $this->processPendingRedirect($statusCode); + } elseif (in_array($statusCode, [ + BuckarooStatusCode::ORDER_FAILED, + BuckarooStatusCode::FAILED, + BuckarooStatusCode::REJECTED, + BuckarooStatusCode::CANCELLED_BY_USER + ])) { + return $this->handleFailed($statusCode); + } + + return $this->_response; } /** - * Add error message to be displayed to the user + * Processes a successful redirect based on the given status code. * - * @param string $message + * - Sends a Klarna KP order confirmation using the status code. + * - Sets the last quote and order. + * - Returns a successful redirect response. * - * @return void + * @param $statusCode + * @return ResponseInterface + * @throws \Exception */ - public function addErrorMessage(string $message) + private function processSucceededRedirect($statusCode): ResponseInterface { - $this->messageManager->addErrorMessage($message); + $this->sendKlarnaKpOrderConfirmation($statusCode); + $this->setLastQuoteOrder(); + + return $this->redirectSuccess(); } /** - * Create redirect response + * Sends a Klarna KP order confirmation based on the given status code. * - * @return ResponseInterface + * - Retrieves the payment method and store from the order object. + * - Checks if the order confirmation email has not been sent and if the order confirmation email + * setting is enabled either globally or specifically for the payment method. + * - Validates if the redirect request contains specific post data and additional information related to Klarna KP + * - If all conditions are met, and the status code is SUCCESS, sends the order confirmation email. + * + * @param int $statusCode The status code representing the result of a payment or related process. + * @return void + * @throws \Exception If an exception occurs within the called methods. */ - protected function redirectToCheckout() + private function sendKlarnaKpOrderConfirmation(int $statusCode): void { - $this->logger->addDebug('start redirectToCheckout'); - if (!$this->customerSession->isLoggedIn()) { - $this->logger->addDebug('not isLoggedIn'); - if ($this->order->getCustomerId() > 0) { - $this->logger->addDebug('getCustomerId > 0'); - try { - $customer = $this->customerRepository->getById($this->order->getCustomerId()); - $this->customerSession->setCustomerDataAsLoggedIn($customer); + $paymentMethod = $this->payment->getMethodInstance(); + $store = $this->order->getStore(); - if (!$this->checkoutSession->getLastRealOrderId() && $this->order->getIncrementId()) { - $this->checkoutSession->setLastRealOrderId($this->order->getIncrementId()); - $this->logger->addDebug(__METHOD__ . '|setLastRealOrderId|'); - $this->checkoutSession->restoreQuote(); - $this->logger->addDebug(__METHOD__ . '|restoreQuote|'); - } elseif ($this->pushRequst->hasPostData('primary_service', 'IDIN')) { - $this->checkoutSession->restoreQuote(); - } - } catch (\Exception $e) { - $this->logger->addError('Could not load customer'); - } + if (!$this->order->getEmailSent() + && ( + $this->accountConfig->getOrderConfirmationEmail($store) === "1" + || $paymentMethod->getConfigData('order_email', $store) === "1" + ) + ) { + $isKlarnaKpReserve = ($this->redirectRequest->hasPostData('primary_service', 'KlarnaKp') + && $this->redirectRequest->hasAdditionalInformation('service_action_from_magento', 'reserve') + && !empty($this->redirectRequest->getServiceKlarnakpReservationnumber())); + + if (!($this->redirectRequest->hasAdditionalInformation('initiated_by_magento', 1) + && $isKlarnaKpReserve + && $statusCode == BuckarooStatusCode::SUCCESS) + ) { + $this->logger->addDebug(__METHOD__ . '|sendemail|'); + $this->orderRequestService->sendOrderEmail($this->order, true); } } - $this->logger->addDebug('ready for redirect'); - return $this->handleProcessedResponse('checkout', ['_query' => ['bk_e' => 1]]); } /** - * Load order by invoice number, order number or by transaction key + * Sets the last quote and order information in the checkout session. + * + * - Logs the current status of the last successful quote ID, last quote ID, last order ID + * - If the last successful quote ID, last quote ID, last order ID, or last real order ID is not set + * in the checkout session, it updates them with the corresponding information from the order object. * - * @throws Exception + * @return void */ - private function loadOrder() + private function setLastQuoteOrder(): void { - $brqOrderId = false; - - if (!empty($this->pushRequst->getInvoiceNumber())) { - $brqOrderId = $this->pushRequst->getInvoiceNumber(); + $this->logger->addDebug(__METHOD__ . '|51|' . var_export([ + $this->checkoutSession->getLastSuccessQuoteId(), + $this->checkoutSession->getLastQuoteId(), + $this->checkoutSession->getLastOrderId(), + $this->checkoutSession->getLastRealOrderId(), + $this->order->getQuoteId(), + $this->order->getId(), + $this->order->getIncrementId(), + ], true)); + + if (!$this->checkoutSession->getLastSuccessQuoteId() && $this->order->getQuoteId()) { + $this->logger->addDebug(__METHOD__ . '|52|'); + $this->checkoutSession->setLastSuccessQuoteId($this->order->getQuoteId()); } - - if (!empty($this->pushRequst->getOrderNumber())) { - $brqOrderId = $this->pushRequst->getOrderNumber(); + if (!$this->checkoutSession->getLastQuoteId() && $this->order->getQuoteId()) { + $this->logger->addDebug(__METHOD__ . '|53|'); + $this->checkoutSession->setLastQuoteId($this->order->getQuoteId()); } - - $this->order->loadByIncrementId($brqOrderId); - - if (!$this->order->getId()) { - $this->logger->addDebug('Order could not be loaded by brq_invoicenumber or brq_ordernumber'); - $this->order = $this->getOrderByTransactionKey(); + if (!$this->checkoutSession->getLastOrderId() && $this->order->getId()) { + $this->logger->addDebug(__METHOD__ . '|54|'); + $this->checkoutSession->setLastOrderId($this->order->getId()); + } + if (!$this->checkoutSession->getLastRealOrderId() && $this->order->getIncrementId()) { + $this->logger->addDebug(__METHOD__ . '|55|'); + $this->checkoutSession->setLastRealOrderId($this->order->getIncrementId()); } } /** - * Get order by transaction key + * Redirect to Success url, which means everything seems to be going fine * - * @return \Magento\Sales\Model\Order\Payment - * @throws Exception + * @return ResponseInterface */ - private function getOrderByTransactionKey() + protected function redirectSuccess(): ResponseInterface { - $trxId = ''; + $this->logger->addDebug(__METHOD__ . '|1|'); - if (!empty($this->pushRequst->getTransactions())) { - $trxId = $this->pushRequst->getTransactions(); - } + $this->eventManager->dispatch('buckaroo_process_redirect_success_before'); - if (!empty($this->pushRequst->getDatarequest())) { - $trxId = $this->pushRequst->getDatarequest(); - } + $store = $this->order->getStore(); + + /** + * @noinspection PhpUndefinedMethodInspection + */ + $url = $this->accountConfig->getSuccessRedirect($store); - $this->transaction->load($trxId, 'txn_id'); - $order = $this->transaction->getOrder(); + $this->addSuccessMessage(__('Your order has been placed successfully.')); - if (!$order) { - throw new Exception(__('There was no order found by transaction Id')); - } + $this->quote->setReservedOrderId(null); + + $this->redirectSuccessApplePay(); - return $order; + $this->logger->addDebug(__METHOD__ . '|2|' . var_export($url, true)); + + return $this->handleProcessedResponse($url); } /** - * Get order + * Add success message to be displayed to the user * - * @return \Magento\Sales\Api\Data\OrderInterface + * @param string|Phrase $message + * + * @return void */ - public function getOrder() + public function addSuccessMessage($message): void { - return $this->order; + $this->messageManager->addSuccessMessage($message); } /** - * Set flag if user is on the payment provider page + * Redirect if the transaction is of the success Apple Pay type * - * @param OrderPaymentInterface $payment * @return void + */ + protected function redirectSuccessApplePay(): void + { + if ($this->redirectRequest->hasPostData('payment_method', 'applepay') + && $this->redirectRequest->hasPostData('status_code', '190') + && $this->redirectRequest->hasPostData('test', 'true') + ) { + $this->logger->addDebug(__METHOD__); + + $this->checkoutSession + ->setLastQuoteId($this->order->getQuoteId()) + ->setLastSuccessQuoteId($this->order->getQuoteId()) + ->setLastOrderId($this->order->getId()) + ->setLastRealOrderId($this->order->getIncrementId()) + ->setLastOrderStatus($this->order->getStatus()); + } + } + + /** + * Processes a pending redirect based on the given status code. + * + * - If the order can be invoiced, it sets the 'Pending payment status' and saves the order. + * - Sends a Klarna KP order confirmation using the status code. + * - If the redirect request does not contain specific post data for the 'sofortueberweisung' payment method, + * it adds an error message, removes an Amasty gift card if failed, and redirect to home. + * - Sets the last quote and order. + * - Returns a successful redirect response. + * + * @param $statusCode + * @return ResponseInterface + * @throws LocalizedException * @throws \Exception */ - protected function setPaymentOutOfTransit(OrderPaymentInterface $payment) + private function processPendingRedirect($statusCode): ResponseInterface { - $payment->setAdditionalInformation(BuckarooAdapter::BUCKAROO_PAYMENT_IN_TRANSIT, false)->save(); + if ($this->order->canInvoice()) { + $this->logger->addDebug(__METHOD__ . '|33|'); + $pendingStatus = $this->orderStatusFactory->get( + BuckarooStatusCode::PENDING_PROCESSING, + $this->order + ); + if ($pendingStatus) { + $this->logger->addDebug(__METHOD__ . '|34|' . var_export($pendingStatus, true)); + $this->order->setStatus($pendingStatus); + $this->order->save(); + } + } + + $this->sendKlarnaKpOrderConfirmation($statusCode); + + if (!$this->redirectRequest->hasPostData('payment_method', 'sofortueberweisung')) { + $this->addErrorMessage( + __(self::GENERAL_ERROR_MESSAGE) + ); + $this->logger->addDebug(__METHOD__ . '|5|'); + + $this->removeCoupon(); + $this->removeAmastyGiftcardOnFailed(); + + return $this->handleProcessedResponse('/'); + } + + $this->setLastQuoteOrder(); + + return $this->redirectSuccess(); + } + + /** + * Add error message to be displayed to the user + * + * @param string|Phrase $message + * + * @return void + */ + public function addErrorMessage($message): void + { + $this->messageManager->addErrorMessage($message); } /** @@ -603,7 +534,7 @@ protected function setPaymentOutOfTransit(OrderPaymentInterface $payment) * * @return void */ - protected function removeAmastyGiftcardOnFailed() + protected function removeAmastyGiftcardOnFailed(): void { if (class_exists(\Amasty\GiftCardAccount\Model\GiftCardAccount\Repository::class)) { $giftcardAccountRepository = $this->_objectManager->get( @@ -632,79 +563,19 @@ protected function removeAmastyGiftcardOnFailed() } } - /** - * Redirect to Success url, which means everything seems to be going fine - * - * @return ResponseInterface - */ - protected function redirectSuccess() - { - $this->logger->addDebug(__METHOD__ . '|1|'); - - $this->eventManager->dispatch('buckaroo_process_redirect_success_before'); - - $store = $this->order->getStore(); - - /** - * @noinspection PhpUndefinedMethodInspection - */ - $url = $this->accountConfig->getSuccessRedirect($store); - - $this->addSuccessMessage(__('Your order has been placed successfully.')); - - $this->quote->setReservedOrderId(null); - - if (!empty($this->pushRequst->getPaymentMethod()) - && - ($this->pushRequst->getPaymentMethod() == 'applepay') - && - !empty($this->pushRequst->getStatusCode()) - && - ($this->pushRequst->getStatusCode() == '190') - && - !empty($this->pushRequst->getTest()) - && - ($this->pushRequst->getTest() == 'true') - ) { - $this->redirectSuccessApplePay(); - } - - $this->logger->addDebug(__METHOD__ . '|2|' . var_export($url, true)); - - return $this->handleProcessedResponse($url); - } - - /** - * Redirect if the transaction is of the success Apple Pay type - * - * @return void - */ - protected function redirectSuccessApplePay() - { - $this->logger->addDebug(__METHOD__); - - $this->checkoutSession - ->setLastQuoteId($this->order->getQuoteId()) - ->setLastSuccessQuoteId($this->order->getQuoteId()) - ->setLastOrderId($this->order->getId()) - ->setLastRealOrderId($this->order->getIncrementId()) - ->setLastOrderStatus($this->order->getStatus()); - } - /** * Handle failed transactions * * @param int|null $statusCode * @return ResponseInterface - * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws \Magento\Framework\Exception\NoSuchEntityException|\Exception */ - protected function handleFailed($statusCode) + protected function handleFailed($statusCode): ResponseInterface { $this->logger->addDebug(__METHOD__ . '|7|'); $this->eventManager->dispatch('buckaroo_process_handle_failed_before'); - $this->removeCoupon(); $this->removeAmastyGiftcardOnFailed(); if (!$this->getSkipHandleFailedRecreate() @@ -718,37 +589,17 @@ protected function handleFailed($statusCode) * 2) cancel the order we had to create to even get here * 3) redirect back to the checkout page to offer the user feedback & the option to try again */ - - // StatusCode specified error messages - $statusCodeAddErrorMessage = []; - $statusCodeAddErrorMessage[$this->helper->getStatusCode('BUCKAROO_MAGENTO2_ORDER_FAILED')] = - 'Unfortunately an error occurred while processing your payment. Please try again. If this' . - ' error persists, please choose a different payment method.'; - $statusCodeAddErrorMessage[$this->helper->getStatusCode('BUCKAROO_MAGENTO2_STATUSCODE_FAILED')] = - 'Unfortunately an error occurred while processing your payment. Please try again. If this' . - ' error persists, please choose a different payment method.'; - $statusCodeAddErrorMessage[$this->helper->getStatusCode('BUCKAROO_MAGENTO2_STATUSCODE_REJECTED')] = - 'Unfortunately an error occurred while processing your payment. Please try again. If this' . - ' error persists, please choose a different payment method.'; - $statusCodeAddErrorMessage[ - $this->helper->getStatusCode('BUCKAROO_MAGENTO2_STATUSCODE_CANCELLED_BY_USER') - ] = 'According to our system, you have canceled the payment. If this' . - ' is not the case, please contact us.'; - - $this->addErrorMessage( - __( - $statusCodeAddErrorMessage[$statusCode] - ) - ); + $this->addErrorMessageByStatus($statusCode); //skip cancel order for PPE - if (!empty($this->pushRequst->getAdditionalInformation('frompayperemail'))) { + if (!empty($this->redirectRequest->getAdditionalInformation('frompayperemail'))) { return $this->redirectFailure(); } if (!$this->cancelOrder($statusCode)) { $this->logger->addError('Could not cancel the order.'); } + $this->logger->addDebug(__METHOD__ . '|8|'); return $this->redirectFailure(); } @@ -763,39 +614,40 @@ public function getSkipHandleFailedRecreate() return false; } + /** + * Adds an error message to the session based on the given status code. + * + * @param int $statusCode + * @return void + */ + public function addErrorMessageByStatus(int $statusCode): void + { + $statusCodeAddErrorMessage = []; + $statusCodeAddErrorMessage[BuckarooStatusCode::ORDER_FAILED] = __(self::GENERAL_ERROR_MESSAGE); + $statusCodeAddErrorMessage[BuckarooStatusCode::FAILED] = __(self::GENERAL_ERROR_MESSAGE); + $statusCodeAddErrorMessage[BuckarooStatusCode::REJECTED] = __(self::GENERAL_ERROR_MESSAGE); + $statusCodeAddErrorMessage[BuckarooStatusCode::CANCELLED_BY_USER] + = __('According to our system, you have canceled the payment. If this is not the case, please contact us.'); + + $this->addErrorMessage( + __( + $statusCodeAddErrorMessage[$statusCode] + ) + ); + } + /** * Redirect to Failure url, which means we've got a problem * * @return ResponseInterface + * @throws \Exception */ - protected function redirectFailure() + protected function redirectFailure(): ResponseInterface { $store = $this->order->getStore(); $this->logger->addDebug('start redirectFailure'); if ($this->accountConfig->getFailureRedirectToCheckout($store)) { - $this->logger->addDebug('getFailureRedirectToCheckout'); - if (!$this->customerSession->isLoggedIn() && ($this->order->getCustomerId() > 0)) { - $this->logger->addDebug('not isLoggedIn'); - $this->logger->addDebug('getCustomerId > 0'); - try { - $customer = $this->customerRepository->getById($this->order->getCustomerId()); - $this->customerSession->setCustomerDataAsLoggedIn($customer); - - if (!$this->checkoutSession->getLastRealOrderId() && $this->order->getIncrementId()) { - $this->checkoutSession->setLastRealOrderId($this->order->getIncrementId()); - $this->logger->addDebug(__METHOD__ . '|setLastRealOrderId|'); - if (!$this->getSkipHandleFailedRecreate()) { - $this->checkoutSession->restoreQuote(); - $this->logger->addDebug(__METHOD__ . '|restoreQuote|'); - } - } - $this->setSkipHandleFailedRecreate(); - } catch (\Exception $e) { - $this->logger->addError('Could not load customer'); - } - } - $this->logger->addDebug('ready for redirect'); - return $this->handleProcessedResponse('checkout', ['_fragment' => 'payment', '_query' => ['bk_e' => 1]]); + return $this->redirectOnCheckoutForFailedTransaction(); } /** @@ -806,6 +658,47 @@ protected function redirectFailure() return $this->handleProcessedResponse($url); } + /** + * Redirects to the checkout page for a failed transaction. + * + * - Logs the attempt to redirect to checkout for a failed transaction. + * - If the customer is not logged in, and there's an associated customer ID with the order, + * it attempts to retrieve the customer, log them in, and set necessary session data. + * - If the last real order ID is not set in the checkout session, and the order has an increment ID, + * it sets the last real order ID and may restore the quote. + * - Finally, it handles the processed response for a redirect to the checkout page, specifically + * to the payment section, with a query parameter indicating an error. + * + * @return ResponseInterface + * @throws \Exception + */ + private function redirectOnCheckoutForFailedTransaction(): ResponseInterface + { + $this->logger->addDebug('getFailureRedirectToCheckout'); + if (!$this->customerSession->isLoggedIn() && ($this->order->getCustomerId() > 0)) { + $this->logger->addDebug('not isLoggedIn'); + $this->logger->addDebug('getCustomerId > 0'); + try { + $customer = $this->customerRepository->getById($this->order->getCustomerId()); + $this->customerSession->setCustomerDataAsLoggedIn($customer); + + if (!$this->checkoutSession->getLastRealOrderId() && $this->order->getIncrementId()) { + $this->checkoutSession->setLastRealOrderId($this->order->getIncrementId()); + $this->logger->addDebug(__METHOD__ . '|setLastRealOrderId|'); + if (!$this->getSkipHandleFailedRecreate()) { + $this->checkoutSession->restoreQuote(); + $this->logger->addDebug(__METHOD__ . '|restoreQuote|'); + } + } + $this->setSkipHandleFailedRecreate(); + } catch (\Exception $e) { + $this->logger->addError('Could not load customer'); + } + } + $this->logger->addDebug('ready for redirect'); + return $this->handleProcessedResponse('checkout', ['_fragment' => 'payment', '_query' => ['bk_e' => 1]]); + } + /** * Set skip recreating quote on failed transaction * @@ -821,12 +714,23 @@ public function setSkipHandleFailedRecreate() * * @param int|null $statusCode * @return bool + * @throws LocalizedException */ - protected function cancelOrder($statusCode) + protected function cancelOrder(?int $statusCode): bool { return $this->orderService->cancel($this->order, $statusCode); } + /** + * Get order + * + * @return OrderInterface + */ + public function getOrder() + { + return $this->order; + } + /** * Remove coupon from failed order if magento enterprise * @@ -860,6 +764,37 @@ protected function removeCoupon() */ public function getResponseParameters() { - return $this->pushRequst->getData(); + return $this->redirectRequest->getData(); + } + + /** + * Create redirect response + * + * @return ResponseInterface + */ + protected function redirectToCheckout(): ResponseInterface + { + $this->logger->addDebug('start redirectToCheckout'); + if (!$this->customerSession->isLoggedIn()) { + $this->logger->addDebug('not isLoggedIn'); + if ($this->order->getCustomerId() > 0) { + $this->logger->addDebug('getCustomerId > 0'); + try { + $customer = $this->customerRepository->getById($this->order->getCustomerId()); + $this->customerSession->setCustomerDataAsLoggedIn($customer); + + if (!$this->checkoutSession->getLastRealOrderId() && $this->order->getIncrementId()) { + $this->checkoutSession->setLastRealOrderId($this->order->getIncrementId()); + $this->logger->addDebug(__METHOD__ . '|setLastRealOrderId|'); + $this->checkoutSession->restoreQuote(); + $this->logger->addDebug(__METHOD__ . '|restoreQuote|'); + } + } catch (\Exception $e) { + $this->logger->addError('Could not load customer'); + } + } + } + $this->logger->addDebug('ready for redirect'); + return $this->handleProcessedResponse('checkout', ['_query' => ['bk_e' => 1]]); } } diff --git a/Gateway/Request/IdinDataBuilder.php b/Gateway/Request/IdinDataBuilder.php index 3fb085f15..0c0f35ea5 100644 --- a/Gateway/Request/IdinDataBuilder.php +++ b/Gateway/Request/IdinDataBuilder.php @@ -120,7 +120,7 @@ public function getReturnUrl(): string { if ($this->returnUrl === null) { $url = $this->urlBuilder->setScope($this->store->getId()); - $url = $url->getRouteUrl('buckaroo/redirect/process') . '?form_key=' . $this->getFormKey(); + $url = $url->getRouteUrl('buckaroo/redirect/idinProcess') . '?form_key=' . $this->getFormKey(); $this->setReturnUrl($url); } diff --git a/Gateway/Request/SaveIssuerDataBuilder.php b/Gateway/Request/SaveIssuerDataBuilder.php index 738e903ad..9d2544776 100644 --- a/Gateway/Request/SaveIssuerDataBuilder.php +++ b/Gateway/Request/SaveIssuerDataBuilder.php @@ -63,7 +63,7 @@ public function build(array $buildSubject): array * * @return void */ - public function saveLastUsedIssuer(OrderPaymentInterface|InfoInterface $payment): void + public function saveLastUsedIssuer($payment): void { /** @var Order $order */ $order = $payment->getOrder(); diff --git a/Gateway/Response/RefundHandler.php b/Gateway/Response/RefundHandler.php index 0ca252e6f..a2c117e22 100644 --- a/Gateway/Response/RefundHandler.php +++ b/Gateway/Response/RefundHandler.php @@ -25,6 +25,7 @@ use Buckaroo\Magento2\Helper\Data; use Buckaroo\Magento2\Logging\Log as BuckarooLog; use Buckaroo\Magento2\Model\Push; +use Buckaroo\Magento2\Model\Push\DefaultProcessor; use Buckaroo\Transaction\Response\TransactionResponse; use Magento\Framework\App\ResourceConnection; use Magento\Framework\Message\ManagerInterface as MessageManager; @@ -102,13 +103,13 @@ public function refundTransactionSdk( ) { $this->buckarooLog->addDebug(__METHOD__ . '|10|'); $transactionKeysArray = $payment->getAdditionalInformation( - Push::BUCKAROO_RECEIVED_TRANSACTIONS_STATUSES + DefaultProcessor::BUCKAROO_RECEIVED_TRANSACTIONS_STATUSES ); foreach ($responseData->getRelatedTransactions() as $relatedTransaction) { $transactionKeysArray[$relatedTransaction['RelatedTransactionKey']] = $responseData->getStatusCode(); } $payment->setAdditionalInformation( - Push::BUCKAROO_RECEIVED_TRANSACTIONS_STATUSES, + DefaultProcessor::BUCKAROO_RECEIVED_TRANSACTIONS_STATUSES, $transactionKeysArray ); $connection = $this->resourceConnection->getConnection(); diff --git a/Helper/PaymentGroupTransaction.php b/Helper/PaymentGroupTransaction.php index 52ea8d561..8b75f9d56 100644 --- a/Helper/PaymentGroupTransaction.php +++ b/Helper/PaymentGroupTransaction.php @@ -326,12 +326,8 @@ public function setGroupTransactionsStatus(string $groupTransactionId, string $s ->getConnection() ->update( $this->resourceModel->getTable('buckaroo_magento2_group_transaction'), - [ - 'status' => $status - ], - [ - 'relatedtransaction = ?' => $groupTransactionId - ] + ['status' => $status], + ['relatedtransaction = ?' => $groupTransactionId] ); } } diff --git a/Model/BuckarooStatusCode.php b/Model/BuckarooStatusCode.php new file mode 100644 index 000000000..91d04fa14 --- /dev/null +++ b/Model/BuckarooStatusCode.php @@ -0,0 +1,133 @@ + 'Success', + 490 => 'Payment failure', + 491 => 'Validation error', + 492 => 'Technical error', + 690 => 'Payment rejected', + 790 => 'Waiting for user input', + 791 => 'Waiting for processor', + 792 => 'Waiting on consumer action', + 793 => 'Payment on hold', + 890 => 'Cancelled by consumer', + 891 => 'Cancelled by merchant' + ]; + + /** + * Buckaroo_Magento2 status codes + * + * @var array $statusCode + */ + private array $statusCodes = [ + 'BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS' => 190, + 'BUCKAROO_MAGENTO2_STATUSCODE_FAILED' => 490, + 'BUCKAROO_MAGENTO2_STATUSCODE_VALIDATION_FAILURE' => 491, + 'BUCKAROO_MAGENTO2_STATUSCODE_TECHNICAL_ERROR' => 492, + 'BUCKAROO_MAGENTO2_STATUSCODE_REJECTED' => 690, + 'BUCKAROO_MAGENTO2_STATUSCODE_WAITING_ON_USER_INPUT' => 790, + 'BUCKAROO_MAGENTO2_STATUSCODE_PENDING_PROCESSING' => 791, + 'BUCKAROO_MAGENTO2_STATUSCODE_WAITING_ON_CONSUMER' => 792, + 'BUCKAROO_MAGENTO2_STATUSCODE_PAYMENT_ON_HOLD' => 793, + 'BUCKAROO_MAGENTO2_STATUSCODE_PENDING_APPROVAL' => 794, + 'BUCKAROO_MAGENTO2_STATUSCODE_CANCELLED_BY_USER' => 890, + 'BUCKAROO_MAGENTO2_STATUSCODE_CANCELLED_BY_MERCHANT' => 891, + + /** + * Codes below are created by dev, not by Buckaroo. + */ + 'BUCKAROO_MAGENTO2_ORDER_FAILED' => 11014, + ]; + + /** + * Get Response Message by Response Code + * + * @param int $responseCode + * @return string + */ + public function getResponseMessage(int $responseCode): string + { + return self::BPE_RESPONSE_MESSAGES[$responseCode] ?? 'Onbekende responsecode: ' . $responseCode; + } + + /** + * Return the requested status key with the value, or null if not found + * + * @param int $responseCode + * @return string + */ + public function getStatusKey(int $responseCode): string + { + $statusKey = array_search($responseCode, $this->statusCodes); + return $statusKey ?: 'BUCKAROO_MAGENTO2_STATUSCODE_NEUTRAL'; + } + + /** + * Get failed statuses + * + * @return string[] + */ + public function getFailedStatuses(): array + { + return [ + 'BUCKAROO_MAGENTO2_STATUSCODE_TECHNICAL_ERROR', + 'BUCKAROO_MAGENTO2_STATUSCODE_VALIDATION_FAILURE', + 'BUCKAROO_MAGENTO2_STATUSCODE_CANCELLED_BY_MERCHANT', + 'BUCKAROO_MAGENTO2_STATUSCODE_CANCELLED_BY_USER', + 'BUCKAROO_MAGENTO2_STATUSCODE_FAILED', + 'BUCKAROO_MAGENTO2_STATUSCODE_REJECTED' + ]; + } + + /** + * Get pending statuses + * + * @return string[] + */ + public function getPendingStatuses(): array + { + return [ + 'BUCKAROO_MAGENTO2_STATUSCODE_PAYMENT_ON_HOLD', + 'BUCKAROO_MAGENTO2_STATUSCODE_WAITING_ON_CONSUMER', + 'BUCKAROO_MAGENTO2_STATUSCODE_PENDING_PROCESSING', + 'BUCKAROO_MAGENTO2_STATUSCODE_WAITING_ON_USER_INPUT' + ]; + } +} \ No newline at end of file diff --git a/Model/ConfigProvider/Method/Applepay.php b/Model/ConfigProvider/Method/Applepay.php index 717da9770..a3f1fe773 100644 --- a/Model/ConfigProvider/Method/Applepay.php +++ b/Model/ConfigProvider/Method/Applepay.php @@ -71,7 +71,7 @@ public function __construct( AllowedCurrencies $allowedCurrencies, PaymentFee $paymentFeeHelper, StoreManagerInterface $storeManager, - Resolver $localeResolver, + Resolver $localeResolver ) { parent::__construct($assetRepo, $scopeConfig, $allowedCurrencies, $paymentFeeHelper); diff --git a/Model/ConfigProvider/Method/PayLink.php b/Model/ConfigProvider/Method/PayLink.php index 246be48ff..dd85ee405 100644 --- a/Model/ConfigProvider/Method/PayLink.php +++ b/Model/ConfigProvider/Method/PayLink.php @@ -24,7 +24,7 @@ use Buckaroo\Magento2\Exception; use Magento\Store\Model\ScopeInterface; -class PayLink extends AbstractConfigProvider +class PayLink extends AbstractConfigProvider { public const CODE = 'buckaroo_magento2_paylink'; diff --git a/Model/Method/BuckarooAdapter.php b/Model/Method/BuckarooAdapter.php index d71877c9b..3d866d950 100644 --- a/Model/Method/BuckarooAdapter.php +++ b/Model/Method/BuckarooAdapter.php @@ -244,29 +244,6 @@ public function canProcessPostData($payment, PushRequestInterface $postData): bo return true; } - /** - * Process custom post data received on push or on redirect - * - * @param OrderPaymentInterface|InfoInterface $payment - * @param array $postData - * @throws \Exception - */ - public function processCustomPostData($payment, array $postData) - { - if ($payment->getMethod() == 'buckaroo_magento2_klarnakp') { - $order = $payment->getOrder(); - - if ($order->getBuckarooReservationNumber()) { - return; - } - - if (isset($postData['brq_service_klarnakp_reservationnumber'])) { - $order->setBuckarooReservationNumber($postData['brq_service_klarnakp_reservationnumber']); - $order->save(); - } - } - } - /** * Can create invoice on push * diff --git a/Model/OrderStatusFactory.php b/Model/OrderStatusFactory.php index 7c9c0e9f2..f4e484205 100644 --- a/Model/OrderStatusFactory.php +++ b/Model/OrderStatusFactory.php @@ -81,12 +81,7 @@ public function get($statusCode, Order $order) * @var BuckarooAdapter $paymentMethodInstance */ $paymentMethodInstance = $order->getPayment()->getMethodInstance(); - if ($paymentMethodInstance instanceof BuckarooAdapter) { - $paymentMethod = $paymentMethodInstance->getCode(); - } else { - $paymentMethod = $paymentMethodInstance->buckarooPaymentMethodCode; - } - + $paymentMethod = $paymentMethodInstance->getCode(); if ($this->configProviderMethodFactory->has($paymentMethod)) { /** diff --git a/Model/Push.php b/Model/Push.php index a3d494557..b78f941b4 100644 --- a/Model/Push.php +++ b/Model/Push.php @@ -17,2292 +17,95 @@ * @copyright Copyright (c) Buckaroo B.V. * @license https://tldrlegal.com/license/mit-license */ +declare(strict_types=1); namespace Buckaroo\Magento2\Model; use Buckaroo\Magento2\Api\PushInterface; use Buckaroo\Magento2\Api\PushRequestInterface; use Buckaroo\Magento2\Exception as BuckarooException; -use Buckaroo\Magento2\Helper\Data; -use Buckaroo\Magento2\Helper\PaymentGroupTransaction; use Buckaroo\Magento2\Logging\Log; -use Buckaroo\Magento2\Model\ConfigProvider\Account; -use Buckaroo\Magento2\Model\ConfigProvider\Method\Afterpay; -use Buckaroo\Magento2\Model\ConfigProvider\Method\Afterpay2; -use Buckaroo\Magento2\Model\ConfigProvider\Method\Afterpay20; -use Buckaroo\Magento2\Model\ConfigProvider\Method\Creditcard; -use Buckaroo\Magento2\Model\ConfigProvider\Method\Factory; -use Buckaroo\Magento2\Model\ConfigProvider\Method\Giftcards; -use Buckaroo\Magento2\Model\ConfigProvider\Method\Klarnakp; -use Buckaroo\Magento2\Model\ConfigProvider\Method\Paypal; -use Buckaroo\Magento2\Model\ConfigProvider\Method\PayPerEmail; -use Buckaroo\Magento2\Model\ConfigProvider\Method\SepaDirectDebit; -use Buckaroo\Magento2\Model\ConfigProvider\Method\Sofortbanking; -use Buckaroo\Magento2\Model\ConfigProvider\Method\Transfer; -use Buckaroo\Magento2\Model\ConfigProvider\Method\Voucher; -use Buckaroo\Magento2\Model\Method\BuckarooAdapter; -use Buckaroo\Magento2\Model\Refund\Push as RefundPush; +use Buckaroo\Magento2\Model\Push\PushProcessorsFactory; +use Buckaroo\Magento2\Model\Push\PushTransactionType; use Buckaroo\Magento2\Model\RequestPush\RequestPushFactory; -use Buckaroo\Magento2\Model\Validator\Push as ValidatorPush; -use Magento\Framework\App\ResourceConnection; -use Magento\Framework\Exception\FileSystemException; -use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Filesystem\DirectoryList; -use Magento\Framework\Filesystem\Driver\File; -use Magento\Framework\Model\AbstractExtensibleModel; -use Magento\Framework\ObjectManagerInterface; -use Magento\Framework\Phrase; -use Magento\Framework\Webapi\Rest\Request; -use Magento\Payment\Model\InfoInterface; -use Magento\Quote\Model\Quote; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\Data\TransactionInterface; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Email\Sender\InvoiceSender; -use Magento\Sales\Model\Order\Email\Sender\OrderSender; -use Magento\Sales\Model\Order\Payment; -use Magento\Sales\Model\Order\Payment\Transaction; +use Buckaroo\Magento2\Service\Push\OrderRequestService; -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - * @SuppressWarnings(PHPMD.TooManyFields) - * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) - */ class Push implements PushInterface { - public const BUCK_PUSH_CANCEL_AUTHORIZE_TYPE = 'I014'; - public const BUCK_PUSH_ACCEPT_AUTHORIZE_TYPE = 'I013'; - public const BUCK_PUSH_GROUPTRANSACTION_TYPE = 'I150'; - public const BUCK_PUSH_IDEAL_PAY = 'C021'; - - public const BUCK_PUSH_TYPE_TRANSACTION = 'transaction_push'; - public const BUCK_PUSH_TYPE_INVOICE = 'invoice_push'; - public const BUCK_PUSH_TYPE_INVOICE_INCOMPLETE = 'incomplete_invoice_push'; - public const BUCK_PUSH_TYPE_DATAREQUEST = 'datarequest_push'; - - public const BUCKAROO_RECEIVED_TRANSACTIONS = 'buckaroo_received_transactions'; - public const BUCKAROO_RECEIVED_TRANSACTIONS_STATUSES = 'buckaroo_received_transactions_statuses'; - - /** - * @var Request $request - */ - public $request; - - /** - * @var ValidatorPush $validator - */ - public $validator; - - /** - * @var Order $order - */ - public $order; - - /** - * @var Transaction - */ - private $transaction; - - /** - * @var OrderSender $orderSender - */ - public $orderSender; - - /** - * @var InvoiceSender $invoiceSender - */ - public $invoiceSender; - - /** - * @var array $postData - */ - public $postData; - - /** - * @var array originalPostData - */ - public $originalPostData; - - /** - * @var $refundPush - */ - public $refundPush; - - /** - * @var Data - */ - public $helper; - /** * @var Log $logging */ - public $logging; - - /** - * @var OrderStatusFactory OrderStatusFactory - */ - public $orderStatusFactory; - - /** - * @var Account - */ - public $configAccount; - - /** - * @var Factory - */ - public $configProviderMethodFactory; - - /** - * @var PaymentGroupTransaction - */ - protected $groupTransaction; - - /** - * @var bool - */ - protected $forceInvoice = false; - - /** - * @var ObjectManagerInterface - */ - protected $objectManager; - - /** - * @var bool - */ - private $dontSaveOrderUponSuccessPush = false; - - /** - * @var ResourceConnection - */ - protected $resourceConnection; - - /** - * @var bool - */ - private $isPayPerEmailB2BModePushInitial = false; - - /** - * @var DirectoryList - */ - protected $dirList; + public Log $logging; /** - * @var Klarnakp + * @var PushRequestInterface */ - private $klarnakpConfig; + public PushRequestInterface $pushRequst; /** - * @var Afterpay20 + * @var PushProcessorsFactory */ - private $afterpayConfig; + private PushProcessorsFactory $pushProcessorsFactory; /** - * @var File + * @var OrderRequestService */ - private $fileSystemDriver; + private OrderRequestService $orderRequestService; /** - * @var PushRequestInterface + * @var PushTransactionType */ - public PushRequestInterface $pushRequst; + private PushTransactionType $pushTransactionType; /** - * @param Order $order - * @param TransactionInterface $transaction - * @param Request $request - * @param ValidatorPush $validator - * @param OrderSender $orderSender - * @param InvoiceSender $invoiceSender - * @param Data $helper - * @param Account $configAccount - * @param RefundPush $refundPush * @param Log $logging - * @param Factory $configProviderMethodFactory - * @param OrderStatusFactory $orderStatusFactory - * @param PaymentGroupTransaction $groupTransaction - * @param ObjectManagerInterface $objectManager - * @param ResourceConnection $resourceConnection - * @param DirectoryList $dirList - * @param Klarnakp $klarnakpConfig - * @param Afterpay20 $afterpayConfig - * @param File $fileSystemDriver * @param RequestPushFactory $requestPushFactory - * @SuppressWarnings(PHPMD.ExcessiveParameterList) + * @param PushProcessorsFactory $pushProcessorsFactory + * @param OrderRequestService $orderRequestService + * @param PushTransactionType $pushTransactionType */ public function __construct( - Order $order, - TransactionInterface $transaction, - Request $request, - ValidatorPush $validator, - OrderSender $orderSender, - InvoiceSender $invoiceSender, - Data $helper, - Account $configAccount, - RefundPush $refundPush, Log $logging, - Factory $configProviderMethodFactory, - OrderStatusFactory $orderStatusFactory, - PaymentGroupTransaction $groupTransaction, - ObjectManagerInterface $objectManager, - ResourceConnection $resourceConnection, - DirectoryList $dirList, - Klarnakp $klarnakpConfig, - Afterpay20 $afterpayConfig, - File $fileSystemDriver, - RequestPushFactory $requestPushFactory + RequestPushFactory $requestPushFactory, + PushProcessorsFactory $pushProcessorsFactory, + OrderRequestService $orderRequestService, + PushTransactionType $pushTransactionType ) { - $this->order = $order; - $this->transaction = $transaction; - $this->request = $request; - $this->validator = $validator; - $this->orderSender = $orderSender; - $this->invoiceSender = $invoiceSender; - $this->helper = $helper; - $this->configAccount = $configAccount; - $this->refundPush = $refundPush; $this->logging = $logging; - $this->configProviderMethodFactory = $configProviderMethodFactory; - $this->orderStatusFactory = $orderStatusFactory; - - $this->groupTransaction = $groupTransaction; - $this->objectManager = $objectManager; - $this->resourceConnection = $resourceConnection; - $this->dirList = $dirList; - $this->klarnakpConfig = $klarnakpConfig; - $this->afterpayConfig = $afterpayConfig; - $this->fileSystemDriver = $fileSystemDriver; $this->pushRequst = $requestPushFactory->create(); + $this->pushProcessorsFactory = $pushProcessorsFactory; + $this->orderRequestService = $orderRequestService; + $this->pushTransactionType = $pushTransactionType; } /** * @inheritdoc * - * @todo Once Magento supports variable parameters, modify this method to no longer require a Request object - * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @return bool + * @throws BuckarooException */ - public function receivePush() + public function receivePush(): bool { - //Start debug mailing/logging with the postdata. + // Log the push request $this->logging->addDebug(__METHOD__ . '|1|' . var_export($this->pushRequst->getOriginalRequest(), true)); - $this->logging->addDebug(__METHOD__ . '|1_2|'); - $lockHandler = $this->lockPushProcessing(); - $this->logging->addDebug(__METHOD__ . '|1_3|'); - - if ($this->isFailedGroupTransaction()) { - $this->handleGroupTransactionFailed(); - return true; - } - - if ($this->isGroupTransactionInfo()) { - if($this->isCanceledGroupTransaction()) { - $this->cancelGroupTransactionOrder(); - return true; - } - if ($this->isGroupTransactionFailed()) { - $this->savePartGroupTransaction(); - } else { - return true; - } - } - - $this->loadOrder(); - - $this->logging->addDebug(__METHOD__ . '|6668| ' . var_export([ - 'orderState' => $this->order->getState(), - 'orderStatus' => $this->order->getStatus(), - 'orderId' => $this->order->getIncrementId() - ], true)); - - if ($this->skipHandlingForFailedGroupTransactions()) { - return true; - } - - if (!$this->isPushNeeded()) { - return true; - } + // Load Order + $order = $this->orderRequestService->getOrderByRequest($this->pushRequst); - $store = $this->order ? $this->order->getStore() : null; - //Check if the push can be processed and if the order can be updated IMPORTANT => use the original post data. + // Validate Signature + $store = $order->getStore(); $validSignature = $this->pushRequst->validate($store); - $transactionType = $this->getTransactionType(); - //Validate status code and return response - $postDataStatusCode = $this->getStatusCode(); - $this->logging->addDebug(__METHOD__ . '|1_5|' . var_export($postDataStatusCode, true)); - - $this->logging->addDebug(__METHOD__ . '|1_10|' . var_export($transactionType, true)); - - $response = $this->validator->validateStatusCode($postDataStatusCode); - - //Check if the push have PayLink - $this->receivePushCheckPayLink($response, $validSignature); - - $payment = $this->order->getPayment(); - - if ($this->pushCheckPayPerEmailCancel($response, $validSignature, $payment)) { - return true; - } - - //Check second push for PayPerEmail - $receivePushCheckPayPerEmailResult = $this->receivePushCheckPayPerEmail($response, $validSignature, $payment); - - $skipFirstPush = $payment->getAdditionalInformation('skip_push'); - - $this->logging->addDebug(__METHOD__ . '|1_20|' . var_export($skipFirstPush, true)); - - /** - * Buckaroo Push is send before Response, for correct flow we skip the first push - * for some payment methods - * - * @todo when buckaroo changes the push / response order this can be removed - */ - if ($skipFirstPush > 0) { - $payment->setAdditionalInformation('skip_push', (int)$skipFirstPush - 1); - $payment->save(); - throw new BuckarooException( - __('Skipped handling this push, first handle response, action will be taken on the next push.') - ); - } - - if ($this->receivePushCheckDuplicates()) { - $this->unlockPushProcessing($lockHandler); - throw new BuckarooException(__('Skipped handling this push, duplicate')); - } - - $this->logging->addDebug(__METHOD__ . '|2|' . var_export($response, true)); - - $canUpdateOrder = $this->canUpdateOrderStatus($response); - - $this->logging->addDebug(__METHOD__ . '|3|' . var_export($canUpdateOrder, true)); - - //Check if the push is a refund request or cancel authorize - if (!empty($this->pushRequst->getAmountCredit())) { - if ($response['status'] !== 'BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS' - && $this->order->isCanceled() - && $this->pushRequst->getTransactionType() == self::BUCK_PUSH_CANCEL_AUTHORIZE_TYPE - && $validSignature - ) { - return $this->processCancelAuthorize(); - } elseif ($response['status'] !== 'BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS' - && !$this->order->hasInvoices() - ) { - throw new BuckarooException( - __('Refund failed ! Status : %1 and the order does not contain an invoice', $response['status']) - ); - } elseif ($response['status'] !== 'BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS' - && $this->order->hasInvoices() - ) { - //don't proceed failed refund push - $this->logging->addDebug(__METHOD__ . '|10|'); - $this->setOrderNotificationNote(__('push notification for refund has no success status, ignoring.')); - return true; - } - return $this->refundPush->receiveRefundPush($this->pushRequst, $validSignature, $this->order); - } - - //Last validation before push can be completed if (!$validSignature) { $this->logging->addDebug('Invalid push signature'); throw new BuckarooException(__('Signature from push is incorrect')); - //If the signature is valid but the order cant be updated, try to add a notification to the order comments. - } elseif ($validSignature && !$canUpdateOrder) { - $this->logging->addDebug('Order can not receive updates'); - if ($receivePushCheckPayPerEmailResult) { - $config = $this->configProviderMethodFactory->get( - PayPerEmail::CODE - ); - if ($config->isEnabledB2B()) { - $this->logging->addDebug(__METHOD__ . '|$this->order->getState()|' . $this->order->getState()); - if ($this->order->getState() === Order::STATE_COMPLETE) { - $this->order->setState(Order::STATE_PROCESSING); - $this->order->save(); - } - return true; - } - } - $this->setOrderNotificationNote(__('The order has already been processed.')); - throw new BuckarooException( - __('Signature from push is correct but the order can not receive updates') - ); - } - - if (!$this->isGroupTransactionInfo()) { - $this->setTransactionKey(); - } - $statusCodeSuccess = $this->helper->getStatusCode('BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS'); - if (!empty($this->pushRequst->getStatusmessage())) { - $this->logging->addDebug(__METHOD__ . '|665| STATE_PROCESSING'); - $this->logging->addDebug(__METHOD__ . '|666| ' . var_export([ - 'orderState' => $this->order->getState(), - 'orderStatus' => $this->order->getStatus(), - 'orderId' => $this->order->getIncrementId(), - 'frompayperemail' => $this->pushRequst->getAdditionalInformation('frompayperemail'), - 'getPartialpayment' => $this->pushRequst->getRelatedtransactionPartialpayment(), - 'statuscode' => $this->pushRequst->getStatusCode() - ], true)); - if ($this->order->getState() === Order::STATE_NEW - && empty($this->pushRequst->getAdditionalInformation('frompayperemail')) - && !$this->pushRequst->hasPostData('brq_transaction_method', 'transfer') - && empty($this->pushRequst->getRelatedtransactionPartialpayment()) - && $this->pushRequst->hasPostData('statuscode', $statusCodeSuccess) - ) { - $this->logging->addDebug(__METHOD__ . '|666| STATE_PROCESSING'); - - $this->order->setState(Order::STATE_PROCESSING); - $this->order->addStatusHistoryComment( - $this->pushRequst->getStatusmessage(), - $this->helper->getOrderStatusByState($this->order, Order::STATE_PROCESSING) - ); - } else { - $this->logging->addDebug(__METHOD__ . '|667| STATE_PROCESSING'); - $this->order->addStatusHistoryComment($this->pushRequst->getStatusmessage()); - } - $this->logging->addDebug(__METHOD__ . '|668| STATE_PROCESSING'); - } - - if ((!in_array($payment->getMethod(), [Giftcards::CODE, Voucher::CODE])) && $this->isGroupTransactionPart()) { - $this->savePartGroupTransaction(); - return true; - } - - - switch ($transactionType) { - case self::BUCK_PUSH_TYPE_INVOICE: - $this->processCm3Push(); - break; - case self::BUCK_PUSH_TYPE_INVOICE_INCOMPLETE: - throw new BuckarooException( - __('Skipped handling this invoice push because it is too soon.') - ); - case self::BUCK_PUSH_TYPE_TRANSACTION: - case self::BUCK_PUSH_TYPE_DATAREQUEST: - default: - $this->processPush($response); - break; - } - - $this->logging->addDebug(__METHOD__ . '|5|'); - if (!$this->dontSaveOrderUponSuccessPush) { - $this->logging->addDebug(__METHOD__ . '|5-1|'); - $this->order->save(); - } - - $this->unlockPushProcessing($lockHandler); - - $this->logging->addDebug(__METHOD__ . '|6|'); - - return true; - } - - /** - * Check for duplicate transaction pushes from Buckaroo and update the payment transaction statuses accordingly. - * - * @param int|null $receivedStatusCode - * @param string|null $trxId - * @return bool - * @throws \Exception - * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - */ - private function receivePushCheckDuplicates(int $receivedStatusCode = null, string $trxId = null): bool - { - $this->logging->addDebug(__METHOD__ . '|1|' . var_export($this->order->getPayment()->getMethod(), true)); - - $save = false; - if (!$receivedStatusCode) { - $save = true; - if (empty($this->pushRequst->getStatusCode())) { - return false; - } - $receivedStatusCode = $this->pushRequst->getStatusCode(); - } - if (!$trxId) { - if (empty($this->pushRequst->getTransactions())) { - return false; - } - $trxId = $this->pushRequst->getTransactions(); - } - $payment = $this->order->getPayment(); - $ignoredPaymentMethods = [ - Giftcards::CODE, - Transfer::CODE - ]; - if ($payment - && $payment->getMethod() - && $receivedStatusCode - && ($this->getTransactionType() == self::BUCK_PUSH_TYPE_TRANSACTION) - && (!in_array($payment->getMethod(), $ignoredPaymentMethods)) - ) { - $this->logging->addDebug(__METHOD__ . '|5|'); - - $receivedTrxStatuses = $payment->getAdditionalInformation( - self::BUCKAROO_RECEIVED_TRANSACTIONS_STATUSES - ); - $this->logging->addDebug(__METHOD__ . '|10|' . - var_export([$receivedTrxStatuses, $receivedStatusCode], true)); - if ($receivedTrxStatuses - && is_array($receivedTrxStatuses) - && !empty($trxId) - && isset($receivedTrxStatuses[$trxId]) - && ($receivedTrxStatuses[$trxId] == $receivedStatusCode) - ) { - $orderStatus = $this->helper->getOrderStatusByState($this->order, Order::STATE_NEW); - $statusCode = $this->helper->getStatusCode('BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS'); - if (($this->order->getState() == Order::STATE_NEW) - && ($this->order->getStatus() == $orderStatus) - && ($receivedStatusCode == $statusCode) - ) { - //allow duplicated pushes for 190 statuses in case if order stills to be new/pending - $this->logging->addDebug(__METHOD__ . '|13|'); - return false; - } - - $this->logging->addDebug(__METHOD__ . '|15|'); - return true; - } - if ($save) { - $this->logging->addDebug(__METHOD__ . '|17|'); - $this->setReceivedTransactionStatuses(); - $payment->save(); - } - } - $this->logging->addDebug(__METHOD__ . '|20|'); - - return false; - } - - /** - * Check if it is needed to handle the push message based on postdata - * - * @return bool - * @throws \Exception - * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - */ - private function isPushNeeded(): bool - { - $this->logging->addDebug(__METHOD__ . '|1|'); - if ($this->pushRequst->hasAdditionalInformation('initiated_by_magento', 1) - && $this->pushRequst->hasAdditionalInformation('service_action_from_magento', ['refund']) - ) { - $statusCodeSuccess = $this->helper->getStatusCode('BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS'); - if ($this->pushRequst->hasPostData('statuscode', $statusCodeSuccess) - && !empty($this->pushRequst->getRelatedtransactionRefund()) - ) { - if ($this->receivePushCheckDuplicates( - $this->helper->getStatusCode('BUCKAROO_MAGENTO2_STATUSCODE_PENDING_APPROVAL'), - $this->pushRequst->getRelatedtransactionRefund() - ) - ) { - $this->logging->addDebug(__METHOD__ . '|4|'); - return true; - } - } - $this->logging->addDebug(__METHOD__ . '|5|'); - return false; - } - - $types = ['capture', 'cancelauthorize', 'cancelreservation']; - if ($this->pushRequst->hasAdditionalInformation('initiated_by_magento', 1) - && $this->pushRequst->hasAdditionalInformation('service_action_from_magento', $types) - && empty($this->pushRequst->getRelatedtransactionRefund()) - ) { - return false; - } - - if ($this->pushRequst->hasAdditionalInformation('initiated_by_magento', 1) - && $this->pushRequst->hasPostData('transaction_method', ['klarnakp', 'KlarnaKp']) - && $this->pushRequst->hasAdditionalInformation('service_action_from_magento', 'pay') - && !empty($this->pushRequst->getServiceKlarnakpCaptureid()) - ) { - return false; - } - - return true; - } - - /** - * Load the order from the Push Data based on the Order Increment ID or transaction key. - * - * @return void - */ - private function loadOrder() - { - $brqOrderId = $this->getOrderIncrementId(); - - //Check if the order can receive further status updates - $this->order->loadByIncrementId((string)$brqOrderId); - - if (!$this->order->getId()) { - $this->logging->addDebug('Order could not be loaded by Invoice Number or Order Number'); - // try to get order by transaction id on payment. - $this->order = $this->getOrderByTransactionKey(); - } - } - - /** - * Save the current order and reload it from the database. - * - * @return void - * @throws \Exception - */ - private function saveAndReloadOrder() - { - $this->order->save(); - $this->loadOrder(); - } - - /** - * Retrieve the status code from the push request based on the transaction type. - * - * @return int|string - * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - */ - private function getStatusCode() - { - $transactionType = $this->getTransactionType(); - $statusCode = 0; - switch ($transactionType) { - case self::BUCK_PUSH_TYPE_TRANSACTION: - case self::BUCK_PUSH_TYPE_DATAREQUEST: - if ($this->pushRequst->getStatusCode() !== null) { - $statusCode = $this->pushRequst->getStatusCode(); - } - break; - case self::BUCK_PUSH_TYPE_INVOICE: - case self::BUCK_PUSH_TYPE_INVOICE_INCOMPLETE: - if (!empty($this->pushRequst->getEventparametersStatuscode())) { - $statusCode = $this->pushRequst->getEventparametersStatuscode(); - } - - if (!empty($this->pushRequst->getEventparametersTransactionstatuscode())) { - $statusCode = $this->pushRequst->getEventparametersTransactionstatuscode(); - } - break; - } - - $statusCodeSuccess = $this->helper->getStatusCode('BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS'); - if ($this->pushRequst->getStatusCode() !== null - && ($this->pushRequst->getStatusCode() == $statusCodeSuccess) - && !$statusCode - ) { - $statusCode = $statusCodeSuccess; - } - - return $statusCode; - } - - /** - * Determine the transaction type based on push request data and the saved invoice key. - * - * @return bool|string - * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - */ - public function getTransactionType() - { - //If an order has an invoice key, then it should only be processed by invoice pushes - $savedInvoiceKey = (string)$this->order->getPayment()->getAdditionalInformation('buckaroo_cm3_invoice_key'); - - if (!empty($this->pushRequst->getInvoicekey()) - && !empty($this->pushRequst->getSchemekey()) - && strlen($savedInvoiceKey) > 0 - ) { - return self::BUCK_PUSH_TYPE_INVOICE; - } - - if (!empty($this->pushRequst->getInvoicekey()) - && !empty($this->pushRequst->getSchemekey()) - && strlen($savedInvoiceKey) == 0 - ) { - return self::BUCK_PUSH_TYPE_INVOICE_INCOMPLETE; - } - - if (!empty($this->pushRequst->getDatarequest())) { - return self::BUCK_PUSH_TYPE_DATAREQUEST; - } - - if (empty($this->pushRequst->getInvoicekey()) - && empty($this->pushRequst->getServiceCreditmanagement3Invoicekey()) - && empty($this->pushRequst->getDatarequest()) - && strlen($savedInvoiceKey) <= 0 - ) { - return self::BUCK_PUSH_TYPE_TRANSACTION; - } - - return false; - } - - /** - * Handle cancelled order authorization and update payment transactions. - * - * @return bool - */ - public function processCancelAuthorize(): bool - { - try { - $this->setTransactionKey(); - } catch (Exception $e) { - $this->logging->addDebug($e->getLogMessage()); - } - - $this->logging->addDebug('Order autorize has been canceld, trying to update payment transactions'); - - return true; - } - - /** - * Process the push according the response status - * - * @param array $response - * @return void - * @throws BuckarooException - * @throws LocalizedException - * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - */ - public function processPush(array $response) - { - $this->logging->addDebug(__METHOD__ . '|1|' . var_export($response['status'], true)); - $payment = $this->order->getPayment(); - - if (!$payment->getMethodInstance()->canProcessPostData($payment, $this->pushRequst)) { - return; - } - - if ($this->giftcardPartialPayment()) { - return; - } - - $newStatus = $this->orderStatusFactory->get($this->pushRequst->getStatusCode(), $this->order); - - $this->logging->addDebug(__METHOD__ . '|5|' . var_export($newStatus, true)); - - if ($this->isPayPerEmailB2BModePushInitial($response)) { - $response['status'] = 'BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS'; - $newStatus = $this->configAccount->getOrderStatusSuccess(); - $this->logging->addDebug(__METHOD__ . '|15|' . var_export([$response['status'], $newStatus], true)); - $this->isPayPerEmailB2BModePushInitial = true; - } - - switch ($response['status']) { - case 'BUCKAROO_MAGENTO2_STATUSCODE_TECHNICAL_ERROR': - case 'BUCKAROO_MAGENTO2_STATUSCODE_VALIDATION_FAILURE': - case 'BUCKAROO_MAGENTO2_STATUSCODE_CANCELLED_BY_MERCHANT': - case 'BUCKAROO_MAGENTO2_STATUSCODE_CANCELLED_BY_USER': - case 'BUCKAROO_MAGENTO2_STATUSCODE_FAILED': - case 'BUCKAROO_MAGENTO2_STATUSCODE_REJECTED': - $this->processFailedPush($newStatus, $response['message']); - break; - case 'BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS': - if ($this->order->getPayment()->getMethod() == Paypal::CODE) { - $paypalConfig = $this->configProviderMethodFactory - ->get(Paypal::CODE); - - /** - * @var \Buckaroo\Magento2\Model\ConfigProvider\Method\Paypal $paypalConfig - */ - $newSellersProtectionStatus = $paypalConfig->getSellersProtectionIneligible(); - if ($paypalConfig->getSellersProtection() && !empty($newSellersProtectionStatus)) { - $newStatus = $newSellersProtectionStatus; - } - } - $this->processSucceededPush($newStatus, $response['message']); - break; - case 'BUCKAROO_MAGENTO2_STATUSCODE_NEUTRAL': - $this->setOrderNotificationNote($response['message']); - break; - case 'BUCKAROO_MAGENTO2_STATUSCODE_PAYMENT_ON_HOLD': - case 'BUCKAROO_MAGENTO2_STATUSCODE_WAITING_ON_CONSUMER': - case 'BUCKAROO_MAGENTO2_STATUSCODE_PENDING_PROCESSING': - case 'BUCKAROO_MAGENTO2_STATUSCODE_WAITING_ON_USER_INPUT': - $this->processPendingPaymentPush(); - break; - } - } - - /** - * Process the Credit Management push, update invoice status and send confirmation mail if required. - * - * @return void - * @throws LocalizedException - */ - public function processCm3Push() - { - $invoiceKey = $this->pushRequst->getInvoicekey(); - $savedInvoiceKey = $this->order->getPayment()->getAdditionalInformation('buckaroo_cm3_invoice_key'); - - if ($invoiceKey != $savedInvoiceKey) { - return; - } - - if ($this->updateCm3InvoiceStatus()) { - $this->sendCm3ConfirmationMail(); - } - } - - /** - * Update the Credit Management invoice status based on push request data and save invoice if required. - * - * @return bool - */ - private function updateCm3InvoiceStatus(): bool - { - $isPaid = filter_var(strtolower($this->pushRequst->getIspaid()), FILTER_VALIDATE_BOOLEAN); - $canInvoice = ($this->order->canInvoice() && !$this->order->hasInvoices()); - - $amount = floatval($this->pushRequst->getAmountDebit()); - $amount = $this->order->getBaseCurrency()->formatTxt($amount); - $statusMessage = 'Payment push status : Creditmanagement invoice with a total amount of ' - . $amount . ' has been paid'; - - if (!$isPaid && !$canInvoice) { - $statusMessage = 'Payment push status : Creditmanagement invoice has been (partially) refunded'; - } - - if (!$isPaid && $canInvoice) { - $statusMessage = 'Payment push status : Waiting for consumer'; - } - - if ($isPaid && $canInvoice) { - $originalKey = BuckarooAdapter::BUCKAROO_ORIGINAL_TRANSACTION_KEY_KEY; - $this->pushRequst->setTransactions($this->order->getPayment()->getAdditionalInformation($originalKey)); - $this->pushRequst->setAmount($this->pushRequst->getAmountDebit()); - - if (!$this->saveInvoice()) { - return false; - } - } - - $this->updateOrderStatus($this->order->getState(), $this->order->getStatus(), $statusMessage); - - return true; - } - - /** - * Sends the CM3 confirmation email if the CM3 status code is 10 and the order email has not been sent. - * - * @return void - * @throws LocalizedException - */ - private function sendCm3ConfirmationMail() - { - $store = $this->order->getStore(); - $cm3StatusCode = 0; - - if (!empty($this->pushRequst->getInvoicestatuscode())) { - $cm3StatusCode = $this->pushRequst->getInvoicestatuscode(); } - $paymentMethod = $this->order->getPayment()->getMethodInstance(); - $configOrderMail = $this->configAccount->getOrderConfirmationEmail($store) - || $paymentMethod->getConfigData('order_email', $store); + // Get Push Transaction Type + $pushTransactionType = $this->pushTransactionType->getPushTransactionType($this->pushRequst, $order); - if (!$this->order->getEmailSent() && $cm3StatusCode == 10 && $configOrderMail) { - $this->orderSender->send($this->order); - } - } - - /** - * Checks if the payment is a partial payment using a gift card. - * - * @return bool - */ - private function giftcardPartialPayment(): bool - { - $payment = $this->order->getPayment(); - - if ($payment->getMethod() != Giftcards::CODE - || (!empty($this->pushRequst->getAmount()) - && $this->pushRequst->getAmount() >= $this->order->getGrandTotal()) - || empty($this->pushRequst->getRelatedtransactionPartialpayment()) - ) { - return false; - } - - if ($this->groupTransaction->isGroupTransaction($this->pushRequst->getInvoiceNumber())) { - return false; - } - - if (!$this->isGroupTransactionInfoType()) { - $payment->setAdditionalInformation( - BuckarooAdapter::BUCKAROO_ORIGINAL_TRANSACTION_KEY_KEY, - $this->pushRequst->getRelatedtransactionPartialpayment() - ); - - $this->addGiftcardPartialPaymentToPaymentInformation(); - } - - return true; - } - - /** - * Adds the gift card partial payment information to the payment's additional information. - * - * @return void - */ - protected function addGiftcardPartialPaymentToPaymentInformation() - { - $payment = $this->order->getPayment(); - - $transactionAmount = $this->pushRequst->getAmount(); - $transactionKey = $this->pushRequst->getTransactions(); - $transactionMethod = $this->pushRequst->getTransactionMethod(); - - $transactionData = $payment->getAdditionalInformation(BuckarooAdapter::BUCKAROO_ALL_TRANSACTIONS); - - $transactionArray = []; - if (is_array($transactionData) && count($transactionData) > 0) { - $transactionArray = $transactionData; - } - - if (!empty($transactionKey) && $transactionAmount > 0) { - $transactionArray[$transactionKey] = [$transactionMethod, $transactionAmount]; - - $payment->setAdditionalInformation( - BuckarooAdapter::BUCKAROO_ALL_TRANSACTIONS, - $transactionArray - ); - } - } - - /** - * Sets the transaction key in the payment's additional information if it's not already set. - * - * @return void - */ - protected function setTransactionKey() - { - $payment = $this->order->getPayment(); - $originalKey = BuckarooAdapter::BUCKAROO_ORIGINAL_TRANSACTION_KEY_KEY; - $transactionKey = $this->getTransactionKey(); - - if (!$payment->getAdditionalInformation($originalKey) && strlen($transactionKey) > 0) { - $payment->setAdditionalInformation($originalKey, $transactionKey); - } - } - - /** - * Store additional transaction information to track multiple payments manually - * Multiple Buckaroo pushes can resolve into incorrect - * - * @return void - */ - protected function setReceivedPaymentFromBuckaroo() - { - if (empty($this->pushRequst->getTransactions())) { - return; - } - - $payment = $this->order->getPayment(); - - if (!$payment->getAdditionalInformation(self::BUCKAROO_RECEIVED_TRANSACTIONS)) { - $payment->setAdditionalInformation( - self::BUCKAROO_RECEIVED_TRANSACTIONS, - [$this->pushRequst->getTransactions() => floatval($this->pushRequst->getAmount())] - ); - } else { - $buckarooTransactionKeysArray = $payment->getAdditionalInformation(self::BUCKAROO_RECEIVED_TRANSACTIONS); - - $buckarooTransactionKeysArray[$this->pushRequst->getTransactions()] = - floatval($this->pushRequst->getAmount()); - - $payment->setAdditionalInformation(self::BUCKAROO_RECEIVED_TRANSACTIONS, $buckarooTransactionKeysArray); - } - } - - /** - * It updates the BUCKAROO_RECEIVED_TRANSACTIONS_STATUSES payment additional information - * with the current received tx status. - * - * @return void - */ - protected function setReceivedTransactionStatuses(): void - { - $txId = $this->pushRequst->getTransactions(); - $statusCode = $this->pushRequst->getStatusCode(); - - if (empty($txId) || empty($statusCode)) { - return; - } - - $payment = $this->order->getPayment(); - - $receivedTxStatuses = $payment->getAdditionalInformation(self::BUCKAROO_RECEIVED_TRANSACTIONS_STATUSES) ?? []; - $receivedTxStatuses[$txId] = $statusCode; - - $payment->setAdditionalInformation(self::BUCKAROO_RECEIVED_TRANSACTIONS_STATUSES, $receivedTxStatuses); - } - - /** - * Retrieves the transaction key from the push request. - * - * @return string - */ - private function getTransactionKey(): string - { - $trxId = ''; - - if (!empty($this->pushRequst->getTransactions())) { - $trxId = $this->pushRequst->getTransactions(); - } - - if (!empty($this->pushRequst->getDatarequest())) { - $trxId = $this->pushRequst->getDatarequest(); - } - - if (!empty($this->pushRequst->getServiceKlarnaAutopaytransactionkey()) - ) { - $trxId = $this->pushRequst->getServiceKlarnaAutopaytransactionkey(); - } - - if (!empty($this->pushRequst->getServiceKlarnakpAutopaytransactionkey()) - ) { - $trxId = $this->pushRequst->getServiceKlarnakpAutopaytransactionkey(); - } - - if (!empty($this->pushRequst->getRelatedtransactionRefund())) { - $trxId = $this->pushRequst->getRelatedtransactionRefund(); - } - - return $trxId; - } - - /** - * Sometimes the push does not contain the order id, when that's the case try to get the order by his payment, - * by using its own transaction key. - * - * @return Payment - * @throws BuckarooException - */ - protected function getOrderByTransactionKey(): Payment - { - $trxId = $this->getTransactionKey(); - - $this->transaction->load($trxId, 'txn_id'); - $order = $this->transaction->getOrder(); - - if (!$order) { - throw new BuckarooException(__('There was no order found by transaction Id')); - } - - return $order; - } - - /** - * Checks if the order can be updated by checking its state and status. - * - * @param array $response - * @return bool - * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - */ - protected function canUpdateOrderStatus(array $response): bool - { - /** - * Types of statusses - */ - $completedStateAndStatus = [Order::STATE_COMPLETE, Order::STATE_COMPLETE]; - $cancelledStateAndStatus = [Order::STATE_CANCELED, Order::STATE_CANCELED]; - $holdedStateAndStatus = [Order::STATE_HOLDED, Order::STATE_HOLDED]; - $closedStateAndStatus = [Order::STATE_CLOSED, Order::STATE_CLOSED]; - /** - * Get current state and status of order - */ - $currentStateAndStatus = [$this->order->getState(), $this->order->getStatus()]; - $this->logging->addDebug(__METHOD__ . '|1|' . var_export($currentStateAndStatus, true)); - - /** - * If the types are not the same and the order can receive an invoice the order can be udpated by BPE. - */ - if ($completedStateAndStatus != $currentStateAndStatus - && $cancelledStateAndStatus != $currentStateAndStatus - && $holdedStateAndStatus != $currentStateAndStatus - && $closedStateAndStatus != $currentStateAndStatus - ) { - return true; - } - - if (($this->order->getState() === Order::STATE_CANCELED) - && ($this->order->getStatus() === Order::STATE_CANCELED) - && ($response['status'] === 'BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS') - && !isset($this->postData['brq_relatedtransaction_partialpayment']) - ) { - $this->logging->addDebug(__METHOD__ . '|2|'); - - $this->order->setState(Order::STATE_NEW); - $this->order->setStatus('pending'); - - foreach ($this->order->getAllItems() as $item) { - $item->setQtyCanceled(0); - } - - $this->forceInvoice = true; - return true; - } - - return false; - } - - /** - * Process the failed push response from Buckaroo and update the order accordingly. - * - * @param string $newStatus - * @param string $message - * @return bool - * @throws LocalizedException - * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - */ - public function processFailedPush(string $newStatus, string $message): bool - { - $this->logging->addDebug(__METHOD__ . '|1|' . var_export($newStatus, true)); - - if (($this->order->getState() === Order::STATE_PROCESSING) - && ($this->order->getStatus() === Order::STATE_PROCESSING) - ) { - //do not update to failed if we had a success already - $this->logging->addDebug(__METHOD__ . '|2|'); - return false; - } - - $description = 'Payment status : ' . $message; - - if (!empty($this->pushRequst->getServiceAntifraudAction())) { - $description .= $this->pushRequst->getServiceAntifraudAction() . - ' ' . - $this->pushRequst->getServiceAntifraudCheck() . - ' ' . - $this->pushRequst->getServiceAntifraudDetails(); - } - - $store = $this->order->getStore(); - - $buckarooCancelOnFailed = $this->configAccount->getCancelOnFailed($store); - - $payment = $this->order->getPayment(); - - if ($buckarooCancelOnFailed && $this->order->canCancel()) { - $this->logging->addDebug(__METHOD__ . '|' . 'Buckaroo push failed : ' . $message . ' : Cancel order.'); - - // BUCKM2-78: Never automatically cancelauthorize via push for afterpay - // setting parameter which will cause to stop the cancel process on - // Buckaroo/Model/Method/BuckarooAdapter.php:880 - $methods = [ - 'buckaroo_magento2_afterpay', - 'buckaroo_magento2_afterpay2', - 'buckaroo_magento2_klarna', - 'buckaroo_magento2_klarnakp' - ]; - if (in_array($payment->getMethodInstance()->getCode(), $methods)) { - $payment->setAdditionalInformation('buckaroo_failed_authorize', 1); - $payment->save(); - } - - $this->updateOrderStatus(Order::STATE_CANCELED, $newStatus, $description); - - try { - $this->order->cancel()->save(); - } catch (\Throwable $t) { - $this->logging->addDebug(__METHOD__ . '|3|'); - // SignifydGateway/Gateway error on line 208" - } - return true; - } - - $this->logging->addDebug(__METHOD__ . '|4|'); - $force = false; - if (($payment->getMethodInstance()->getCode() == 'buckaroo_magento2_mrcash') - && ($this->order->getState() === Order::STATE_NEW) - && ($this->order->getStatus() === 'pending') - ) { - $force = true; - } - $this->updateOrderStatus(Order::STATE_CANCELED, $newStatus, $description, $force); - - return true; - } - - /** - * Process the successful push response from Buckaroo and update the order accordingly. - * - * @param string $newStatus - * @param string $message - * @return bool - * @throws LocalizedException - * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function processSucceededPush(string $newStatus, string $message): bool - { - $this->logging->addDebug(__METHOD__ . '|1|' . var_export($newStatus, true)); - - $amount = $this->order->getTotalDue(); - - if (!empty($this->pushRequst->getAmount())) { - $this->logging->addDebug(__METHOD__ . '|11|'); - $amount = floatval($this->pushRequst->getAmount()); - } - - if (!empty($this->pushRequst->getServiceKlarnaReservationnumber())) { - $this->order->setBuckarooReservationNumber($this->pushRequst->getServiceKlarnaReservationnumber()); - $this->order->save(); - } - - if (!empty($this->pushRequst->getServiceKlarnakpReservationnumber())) { - $this->order->setBuckarooReservationNumber($this->pushRequst->getServiceKlarnakpReservationnumber()); - $this->order->save(); - } - - $store = $this->order->getStore(); - - $payment = $this->order->getPayment(); - - /** - * @var \Magento\Payment\Model\MethodInterface $paymentMethod - */ - $paymentMethod = $payment->getMethodInstance(); - - if (!$this->order->getEmailSent() - && ($this->configAccount->getOrderConfirmationEmail($store) - || $paymentMethod->getConfigData('order_email', $store) - ) - ) { - $this->logging->addDebug(__METHOD__ . '|sendemail|' . - var_export($this->configAccount->getOrderConfirmationEmailSync($store), true)); - $this->orderSender->send($this->order, $this->configAccount->getOrderConfirmationEmailSync($store)); - } - - /** force state eventhough this can lead to a transition of the order - * like new -> processing - */ - $forceState = false; - $state = Order::STATE_PROCESSING; - - $this->logging->addDebug(__METHOD__ . '|2|'); - - if ($paymentMethod->canPushInvoice($this->pushRequst)) { - $this->logging->addDebug(__METHOD__ . '|3|'); - $description = 'Payment status : ' . $message . "
"; - if ($this->pushRequst->hasPostData('transaction_method', 'transfer')) { - //keep amount fetched from brq_amount - $description .= 'Amount of ' . $this->order->getBaseCurrency()->formatTxt($amount) . ' has been paid'; - } else { - $amount = $this->order->getBaseTotalDue(); - $description .= 'Total amount of ' . - $this->order->getBaseCurrency()->formatTxt($amount) . ' has been paid'; - } - } else { - $description = 'Authorization status : ' . $message . "
"; - $description .= 'Total amount of ' . $this->order->getBaseCurrency()->formatTxt($this->order->getTotalDue()) - . ' has been authorized. Please create an invoice to capture the authorized amount.'; - $forceState = true; - } - - if ($this->isPayPerEmailB2BModePushInitial) { - $description = ''; - } - - $this->dontSaveOrderUponSuccessPush = false; - if ($paymentMethod->canPushInvoice($this->pushRequst)) { - $this->logging->addDebug(__METHOD__ . '|4|'); - - if (!$this->isPayPerEmailB2BModePushInitial && $this->isPayPerEmailB2BModePushPaid()) { - $this->logging->addDebug(__METHOD__ . '|4_1|'); - //Fix for suspected fraud when the order currency does not match with the payment's currency - $amount = ($payment->isSameCurrency() && $payment->isCaptureFinal($this->order->getGrandTotal())) ? - $this->order->getGrandTotal() : $this->order->getBaseTotalDue(); - $payment->registerCaptureNotification($amount); - $payment->save(); - $this->order->setState('complete'); - $this->order->addStatusHistoryComment($description, 'complete'); - $this->order->save(); - - if ($transactionKey = $this->getTransactionKey()) { - foreach ($this->order->getInvoiceCollection() as $invoice) { - $invoice->setTransactionId($transactionKey)->save(); - } - } - return true; - } - - if ($this->pushRequst->hasAdditionalInformation('initiated_by_magento', 1) && - ( - $this->pushRequst->hasPostData('transaction_method', 'KlarnaKp') && - $this->pushRequst->hasAdditionalInformation('service_action_from_magento', 'pay') && - empty($this->pushRequst->getServiceKlarnakpReservationnumber()) && - $this->klarnakpConfig->isInvoiceCreatedAfterShipment() - ) || - ( - $this->pushRequst->hasPostData('transaction_method', 'afterpay') && - $this->pushRequst->hasAdditionalInformation('service_action_from_magento', 'capture') && - $this->afterpayConfig->isInvoiceCreatedAfterShipment() - ) - ) { - $this->logging->addDebug(__METHOD__ . '|5_1|'); - $this->dontSaveOrderUponSuccessPush = true; - return true; - } else { - $this->logging->addDebug(__METHOD__ . '|6|'); - - if ($this->pushRequst->hasPostData('transaction_method', 'transfer')) { - //invoice only in case of full or last remained amount - $this->logging->addDebug(__METHOD__ . '|61|' . var_export([ - $this->order->getId(), - $amount, - $this->order->getTotalDue(), - $this->order->getTotalPaid(), - ], true)); - - $saveInvoice = true; - if (($amount < $this->order->getTotalDue()) - || (($amount == $this->order->getTotalDue()) && ($this->order->getTotalPaid() > 0)) - ) { - $this->logging->addDebug(__METHOD__ . '|64|'); - - $forceState = true; - if ($amount < $this->order->getTotalDue()) { - $this->logging->addDebug(__METHOD__ . '|65|'); - $state = Order::STATE_NEW; - $newStatus = $this->orderStatusFactory->get( - $this->helper->getStatusCode('BUCKAROO_MAGENTO2_STATUSCODE_PENDING_PROCESSING'), - $this->order - ); - $saveInvoice = false; - } - - $this->saveAndReloadOrder(); - - $this->order->setTotalDue($this->order->getTotalDue() - $amount); - $this->order->setBaseTotalDue($this->order->getTotalDue() - $amount); - - $totalPaid = $this->order->getTotalPaid() + $amount; - $this->order->setTotalPaid( - $totalPaid > $this->order->getGrandTotal() ? $this->order->getGrandTotal() : $totalPaid - ); - - $baseTotalPaid = $this->order->getBaseTotalPaid() + $amount; - $this->order->setBaseTotalPaid( - $baseTotalPaid > $this->order->getBaseGrandTotal() ? - $this->order->getBaseGrandTotal() : $baseTotalPaid - ); - - $this->saveAndReloadOrder(); - - $connection = $this->resourceConnection->getConnection(); - $connection->update( - $connection->getTableName('sales_order'), - [ - 'total_due' => $this->order->getTotalDue(), - 'base_total_due' => $this->order->getTotalDue(), - 'total_paid' => $this->order->getTotalPaid(), - 'base_total_paid' => $this->order->getBaseTotalPaid(), - ], - $connection->quoteInto('entity_id = ?', $this->order->getId()) - ); - } - - if ($saveInvoice) { - if (!$this->saveInvoice()) { - return false; - } - } - } else { - if (!$this->saveInvoice()) { - return false; - } - } - } - } - - if (!empty($this->pushRequst->getServiceKlarnaAutopaytransactionkey()) - && ($this->pushRequst->getStatusCode() == 190) - ) { - $this->saveInvoice(); - } - - if (!empty($this->pushRequst->getServiceKlarnakpAutopaytransactionkey()) - && ($this->pushRequst->getStatusCode() == 190) - ) { - $this->saveInvoice(); - } - - if ($this->groupTransaction->isGroupTransaction($this->pushRequst->getInvoiceNumber())) { - $forceState = true; - } - - $this->logging->addDebug(__METHOD__ . '|8|'); - - $this->processSucceededPushAuth($payment); - - $this->updateOrderStatus($state, $newStatus, $description, $forceState); - - $this->logging->addDebug(__METHOD__ . '|9|'); - - return true; - } - - /** - * Process the pending payment push response from Buckaroo and update the order accordingly. - * - * @return bool - * @throws LocalizedException - */ - public function processPendingPaymentPush(): bool - { - $this->logging->addDebug(__METHOD__ . '|1|'); - - $store = $this->order->getStore(); - $payment = $this->order->getPayment(); - - $paymentMethod = $payment->getMethodInstance(); - - // Transfer has a slightly different flow where a successful order has a 792 status code instead of an 190 one - if (!$this->order->getEmailSent() - && in_array($payment->getMethod(), [Transfer::CODE, - SepaDirectDebit::CODE, - Sofortbanking::CODE, - PayPerEmail::CODE, - ]) - && ($this->configAccount->getOrderConfirmationEmail($store) - || $paymentMethod->getConfigData('order_email', $store) - ) - ) { - $this->logging->addDebug(__METHOD__ . '|sendemail|'); - $this->orderSender->send($this->order); - } - - return true; - } - - /** - * Try to add a notification note to the order comments. - * - * @param Phrase|string $message - * @throws \Exception - */ - protected function setOrderNotificationNote(string $message) - { - $note = 'Buckaroo attempted to update this order, but failed: ' . $message; - try { - $this->order->addStatusToHistory($note); - $this->order->save(); - } catch (\Exception $e) { - $this->logging->addDebug($e->getLogMessage()); - } - } - - /** - * Updates the order state and add a comment. - * - * @param string $orderState - * @param string $newStatus - * @param string $description - * @param bool $force - * @throws \Exception - */ - protected function updateOrderStatus( - string $orderState, - string $newStatus, - string $description, - bool $force = false - ) { - $this->logging->addDebug(__METHOD__ . '|0|' . var_export([$orderState, $newStatus, $description], true)); - if ($this->order->getState() == $orderState || $force) { - $this->logging->addDebug(__METHOD__ . '|1|'); - $this->logging->addDebug('||| $orderState: ' . '|1|' . $orderState); - if ($this->dontSaveOrderUponSuccessPush) { - $this->order->addCommentToStatusHistory($description) - ->setIsCustomerNotified(false) - ->setEntityName('invoice') - ->setStatus($newStatus) - ->save(); - } else { - $this->order->addCommentToStatusHistory($description, $newStatus); - } - } else { - $this->logging->addDebug(__METHOD__ . '|2|'); - $this->logging->addDebug('||| $orderState: ' . '|2|' . $orderState); - if ($this->dontSaveOrderUponSuccessPush) { - $this->order->addCommentToStatusHistory($description) - ->setIsCustomerNotified(false) - ->setEntityName('invoice') - ->save(); - } else { - $this->order->addCommentToStatusHistory($description); - } - } - } - - /** - * Creates and saves the invoice and adds for each invoice the buckaroo transaction keys - * Only when the order can be invoiced and has not been invoiced before. - * - * @return bool - * @throws BuckarooException - * @throws LocalizedException - * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - */ - protected function saveInvoice(): bool - { - $this->logging->addDebug(__METHOD__ . '|1|'); - if (!$this->forceInvoice - && (!$this->order->canInvoice() || $this->order->hasInvoices())) { - $this->logging->addDebug('Order can not be invoiced'); - //throw new BuckarooException(__('Order can not be invoiced')); - return false; - - } - - $this->logging->addDebug(__METHOD__ . '|5|'); - - /** - * Only when the order can be invoiced and has not been invoiced before. - */ - if (!$this->isGroupTransactionInfoType()) { - $this->addTransactionData(); - } - - /** - * @var Payment $payment - */ - $payment = $this->order->getPayment(); - - $invoiceAmount = 0; - if (!empty($this->pushRequst->getAmount())) { - $invoiceAmount = floatval($this->pushRequst->getAmount()); - } - if (($payment->getMethod() == Giftcards::CODE) - && $invoiceAmount != $this->order->getGrandTotal() - ) { - $this->setReceivedPaymentFromBuckaroo(); - - $payment->registerCaptureNotification($invoiceAmount, true); - $payment->save(); - - $receivedPaymentsArray = $payment->getAdditionalInformation(self::BUCKAROO_RECEIVED_TRANSACTIONS); - - if (!is_array($receivedPaymentsArray)) { - return false; - } - - $payment->capture(); //creates invoice - $payment->save(); - } elseif ($this->isPayPerEmailB2BModePushInitial) { - $this->logging->addDebug(__METHOD__ . '|10|'); - $invoice = $this->order->prepareInvoice()->register(); - $invoice->setOrder($this->order); - $this->order->addRelatedObject($invoice); - $payment->setCreatedInvoice($invoice); - $payment->setShouldCloseParentTransaction(true); - } else { - $this->logging->addDebug(__METHOD__ . '|15|'); - //Fix for suspected fraud when the order currency does not match with the payment's currency - $amount = ($payment->isSameCurrency() - && $payment->isCaptureFinal($this->order->getGrandTotal())) ? - $this->order->getGrandTotal() : $this->order->getBaseTotalDue(); - $payment->registerCaptureNotification($amount); - $payment->save(); - } - - $this->logging->addDebug(__METHOD__ . '|20|'); - - $transactionKey = $this->getTransactionKey(); - - if (strlen($transactionKey) <= 0) { - return true; - } - - $this->logging->addDebug(__METHOD__ . '|25|'); - - /** @var \Magento\Sales\Model\Order\Invoice $invoice */ - foreach ($this->order->getInvoiceCollection() as $invoice) { - $invoice->setTransactionId($transactionKey)->save(); - - if (!empty($this->pushRequst->getInvoiceNumber()) - && $this->groupTransaction->isGroupTransaction($this->pushRequst->getInvoiceNumber())) { - $this->logging->addDebug(__METHOD__ . '|27|'); - $invoice->setState(2); - } - - if (!$invoice->getEmailSent() && $this->configAccount->getInvoiceEmail($this->order->getStore())) { - $this->logging->addDebug(__METHOD__ . '|30|sendinvoiceemail'); - $this->invoiceSender->send($invoice, true); - } - } - - $this->logging->addDebug(__METHOD__ . '|35|'); - - $this->order->setIsInProcess(true); - $this->order->save(); - - $this->dontSaveOrderUponSuccessPush = true; - - return true; - } - - /** - * Adds transaction data to the order payment with the given transaction key and data. - * - * @param bool $transactionKey - * @param bool $data - * @return Payment - * @throws LocalizedException - * @throws \Exception - */ - public function addTransactionData(bool $transactionKey = false, bool $data = false): Payment - { - /** - * @var Payment $payment - */ - $payment = $this->order->getPayment(); - - $transactionKey = $transactionKey ?: $this->getTransactionKey(); - - if (strlen($transactionKey) <= 0) { - throw new \Exception(__('There was no transaction ID found')); - } - - /** - * Save the transaction's response as additional info for the transaction. - */ - $postData = $data ?: $this->pushRequst->getData(); - $rawInfo = $this->helper->getTransactionAdditionalInfo($postData); - - /** - * @noinspection PhpUndefinedMethodInspection - */ - $payment->setTransactionAdditionalInfo( - Transaction::RAW_DETAILS, - $rawInfo - ); - - /** - * Save the payment's transaction key. - */ - /** - * @noinspection PhpUndefinedMethodInspection - */ - if ($this->pushRequst->hasPostData('transaction_method', 'KlarnaKp')) { - $payment->setTransactionId($transactionKey); - } else { - $payment->setTransactionId($transactionKey . '-capture'); - } - /** - * @noinspection PhpUndefinedMethodInspection - */ - $payment->setParentTransactionId($transactionKey); - $payment->setAdditionalInformation( - \Buckaroo\Magento2\Model\Method\BuckarooAdapter::BUCKAROO_ORIGINAL_TRANSACTION_KEY_KEY, - $transactionKey - ); - - return $payment; - } - - private function isGroupTransactionInfoType() - { - if (!empty($this->pushRequst->getTransactionType()) - && ($this->pushRequst->getTransactionType() == self::BUCK_PUSH_GROUPTRANSACTION_TYPE) - ) { - return true; - } - return false; - } - - /** - * Checks if the transaction type is a group transaction. - * - * @return bool - */ - private function isGroupTransactionInfo() - { - $this->logging->addDebug(__METHOD__ . '|1|'); - if ($this->isGroupTransactionInfoType()) { - if ($this->pushRequst->getStatusCode() != - $this->helper->getStatusCode('BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS') - ) { - return true; - } - } - return false; - } - - /** - * Checks if the push request is a group transaction with a non-success status code. - * - * @return false|mixed - */ - private function isGroupTransactionPart() - { - if (!is_null($this->pushRequst->getTransactions())) { - return $this->groupTransaction->getGroupTransactionByTrxId($this->pushRequst->getTransactions()); - } - return false; - } - - /** - * Check if the group transaction has failed. - * - * @return bool - */ - private function isGroupTransactionFailed() - { - if ($this->isGroupTransactionInfoType() - && $this->pushRequst->getStatusCode() == - $this->helper->getStatusCode('BUCKAROO_MAGENTO2_STATUSCODE_FAILED') - ) { - return true; - } - - return false; - } - - /** - * Save the part group transaction. - * - * @return void - */ - private function savePartGroupTransaction() - { - $items = $this->groupTransaction->getGroupTransactionByTrxId($this->pushRequst->getTransactions()); - if (is_array($items) && count($items) > 0) { - foreach ($items as $item) { - $item2['status'] = $this->pushRequst->getStatusCode(); - $item2['entity_id'] = $item['entity_id']; - $this->groupTransaction->updateGroupTransaction($item2); - } - } - } - - /** - * Check if the PayLink payment was successful. - * - * @param array $response - * @param bool $validSignature - * @return bool - * @throws \Exception - */ - private function receivePushCheckPayLink(array $response, bool $validSignature): bool - { - if (!empty($this->pushRequst->getAdditionalInformation('frompaylink')) - && $response['status'] == 'BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS' - && $validSignature - ) { - $payment = $this->order->getPayment(); - $payment->setMethod('buckaroo_magento2_payperemail'); - $payment->save(); - $this->order->save(); - return true; - } - return false; - } - - /** - * Check if the Pay Per Email payment was cancelled. - * - * @param array $response - * @param bool $validSignature - * @param InfoInterface $payment - * @return bool - * @throws BuckarooException - */ - private function pushCheckPayPerEmailCancel(array $response, bool $validSignature, InfoInterface $payment): bool - { - $failedStatuses = [ - 'BUCKAROO_MAGENTO2_STATUSCODE_TECHNICAL_ERROR', - 'BUCKAROO_MAGENTO2_STATUSCODE_VALIDATION_FAILURE', - 'BUCKAROO_MAGENTO2_STATUSCODE_CANCELLED_BY_MERCHANT', - 'BUCKAROO_MAGENTO2_STATUSCODE_CANCELLED_BY_USER', - 'BUCKAROO_MAGENTO2_STATUSCODE_FAILED', - 'BUCKAROO_MAGENTO2_STATUSCODE_REJECTED' - ]; - $status = $this->helper->getStatusByValue($this->pushRequst->getStatusCode() ?? ''); - if ((!empty($this->pushRequst->getAdditionalInformation('frompayperemail')) - || ($payment->getMethod() == 'buckaroo_magento2_payperemail')) - && !empty($this->pushRequst->getTransactionMethod()) - && ((in_array($response['status'], $failedStatuses)) - || (in_array($status, $failedStatuses)) - ) && $validSignature - ) { - $config = $this->configProviderMethodFactory->get(PayPerEmail::CODE); - if ($config->getEnabledCronCancelPPE()) { - return true; - } - } - return false; - } - - /** - * Check if the Pay Per Email payment was successful. - * - * @param array $response - * @param bool $validSignature - * @param InfoInterface $payment - * @return bool - * @throws \Exception - * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - */ - private function receivePushCheckPayPerEmail(array $response, bool $validSignature, InfoInterface $payment) - { - $status = $this->helper->getStatusByValue($this->pushRequst->getStatusCode() ?? ''); - if ((!empty($this->pushRequst->getAdditionalInformation('frompayperemail')) - || ($payment->getMethod() == 'buckaroo_magento2_payperemail')) - && !empty($this->pushRequst->getTransactionMethod()) - && (($response['status'] == 'BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS') - || ($status == 'BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS') - ) && $validSignature - && $this->pushRequst->getTransactionMethod() != 'payperemail') { - $brq_transaction_method = strtolower($this->pushRequst->getTransactionMethod()); - $payment = $this->order->getPayment(); - $payment->setAdditionalInformation('isPayPerEmail', $brq_transaction_method); - - $options = new \Buckaroo\Magento2\Model\Config\Source\PaymentMethods\PayPerEmail(); - foreach ($options->toOptionArray() as $item) { - if (($item['value'] == $brq_transaction_method) && isset($item['code'])) { - $payment->setMethod($item['code']); - $payment->setAdditionalInformation( - BuckarooAdapter::BUCKAROO_ORIGINAL_TRANSACTION_KEY_KEY, - $this->getTransactionKey() - ); - if ($item['code'] == 'buckaroo_magento2_creditcards') { - $payment->setAdditionalInformation('card_type', $brq_transaction_method); - } - } - } - $payment->save(); - $this->order->save(); - return true; - } - - return false; - } - - /** - * Check if the Pay Per Email payment is in B2B mode. - * - * @return bool - * @throws BuckarooException - */ - public function isPayPerEmailB2BModePush(): bool - { - if (!empty($this->pushRequst->getAdditionalInformation('frompayperemail')) - && !empty($this->pushRequst->getTransactionMethod()) - && ($this->pushRequst->getTransactionMethod() == 'payperemail') - ) { - $this->logging->addDebug(__METHOD__ . '|1|'); - $config = $this->configProviderMethodFactory->get(PayPerEmail::CODE); - if ($config->isEnabledB2B()) { - $this->logging->addDebug(__METHOD__ . '|5|'); - return true; - } - } - return false; - } - - /** - * Check if the Pay Per Email payment is in B2C mode. - * - * @return bool - * @throws BuckarooException - */ - public function isPayPerEmailB2CModePush(): bool - { - if (!empty($this->pushRequst->getAdditionalInformation('frompayperemail')) - && !empty($this->pushRequst->getTransactionMethod()) - && ($this->pushRequst->getTransactionMethod() == 'payperemail') - ) { - $this->logging->addDebug(__METHOD__ . '|1|'); - $config = $this->configProviderMethodFactory->get(PayPerEmail::CODE); - if (!$config->isEnabledB2B()) { - $this->logging->addDebug(__METHOD__ . '|5|'); - return true; - } - } - return false; - } - - /** - * Check if the Pay Per Email payment is in B2B mode and in the initial push. - * - * @param array $response - * @return bool - * @throws BuckarooException - */ - public function isPayPerEmailB2BModePushInitial(array $response): bool - { - $this->logging->addDebug(__METHOD__ . '|1|'); - return $this->isPayPerEmailB2BModePush() - && ($response['status'] == 'BUCKAROO_MAGENTO2_STATUSCODE_WAITING_ON_CONSUMER'); - } - - /** - * Check if the Pay Per Email payment is in B2C mode and in the initial push. - * - * @param array $response - * @return bool - * @throws BuckarooException - */ - public function isPayPerEmailB2CModePushInitial(array $response): bool - { - $this->logging->addDebug(__METHOD__ . '|1|'); - return $this->isPayPerEmailB2CModePush() - && ($response['status'] == 'BUCKAROO_MAGENTO2_STATUSCODE_WAITING_ON_CONSUMER'); - } - - /** - * Check if the Pay Per Email payment is in B2B mode and has been paid. - * - * @return bool - * @throws BuckarooException - */ - public function isPayPerEmailB2BModePushPaid(): bool - { - $this->logging->addDebug(__METHOD__ . '|1|'); - return $this->isPayPerEmailB2BModePush(); - } - - /** - * Get the order increment ID based on the invoice number or order number. - * - * @return string|false - */ - private function getOrderIncrementId() - { - $brqOrderId = false; - - if (!empty($this->pushRequst->getInvoiceNumber()) && strlen($this->pushRequst->getInvoiceNumber()) > 0) { - $brqOrderId = $this->pushRequst->getInvoiceNumber(); - } - - if (!empty($this->pushRequst->getOrderNumber()) && strlen($this->pushRequst->getOrderNumber()) > 0) { - $brqOrderId = $this->pushRequst->getOrderNumber(); - } - - return $brqOrderId; - } - - /** - * Get the file path for the lock push processing file. - * - * @return string|false - * @throws FileSystemException - */ - private function getLockPushProcessingFilePath() - { - if ($brqOrderId = $this->getOrderIncrementId()) { - return $this->dirList->getPath('tmp') . DIRECTORY_SEPARATOR . 'bk_push_ppe_' . sha1($brqOrderId); - } else { - return false; - } - } - - /** - * Determine if the lock push processing criteria are met. - * - * @return bool - */ - private function lockPushProcessingCriteria(): bool - { - $statusCodeSuccess = $this->helper->getStatusCode('BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS'); - if (!empty($this->pushRequst->getAdditionalInformation('frompayperemail')) - || ( - ($this->pushRequst->hasPostData('statuscode', $statusCodeSuccess)) - && $this->pushRequst->hasPostData('transaction_method', 'ideal') - && $this->pushRequst->hasPostData('transaction_type', self::BUCK_PUSH_IDEAL_PAY) - ) - ) { - return true; - } else { - return false; - } - } - - /** - * Lock the push processing if criteria are met. - * - * @return resource|void - * @throws FileSystemException - */ - private function lockPushProcessing() - { - if ($this->lockPushProcessingCriteria()) { - $this->logging->addDebug(__METHOD__ . '|1|'); - if ($path = $this->getLockPushProcessingFilePath()) { - if ($fp = $this->fileSystemDriver->fileOpen($path, "w+")) { - $this->fileSystemDriver->fileLock($fp, LOCK_EX); - $this->logging->addDebug(__METHOD__ . '|5|'); - return $fp; - } - } - } - } - - /** - * Unlock the push processing. - * - * @param resource $lockHandler - * @return void - * @throws FileSystemException - */ - private function unlockPushProcessing($lockHandler) - { - if ($this->lockPushProcessingCriteria()) { - $this->logging->addDebug(__METHOD__ . '|1|'); - $this->fileSystemDriver->fileClose($lockHandler); - if (($path = $this->getLockPushProcessingFilePath()) && $this->fileSystemDriver->isExists($path)) { - $this->fileSystemDriver->deleteFile($path); - $this->logging->addDebug(__METHOD__ . '|5|'); - } - } - } - - /** - * Process succeeded push authorization. - * - * @param InfoInterface $payment - * @return void - * @throws \Exception - */ - private function processSucceededPushAuth(InfoInterface $payment) - { - $authPpaymentMethods = [ - Afterpay::CODE, - Afterpay2::CODE, - Afterpay20::CODE, - Creditcard::CODE, - Klarnakp::CODE - ]; - - if (in_array($payment->getMethod(), $authPpaymentMethods)) { - if ((($payment->getMethod() == Klarnakp::CODE) - || ( - !empty($this->pushRequst->getTransactionType()) - && in_array($this->pushRequst->getTransactionType(), ['I038', 'I880']) - ) - ) && !empty($this->pushRequst->getStatusCode()) - && ($this->pushRequst->getStatusCode() == 190) - ) { - $this->logging->addDebug(__METHOD__ . '|88|' . var_export($payment->getMethod(), true)); - $this->order->setState(Order::STATE_PROCESSING); - $this->order->save(); - } - } - } - - /** - * Handle push from main group transaction fail - * - * @return void - */ - protected function handleGroupTransactionFailed() - { - try { - $this->cancelOrder( - $this->postData['brq_invoicenumber'] - ); - $this->groupTransaction->setGroupTransactionsStatus( - $this->postData['brq_transactions'], - $this->postData['brq_statuscode'] - ); - } catch (\Throwable $th) { - $this->logging->addDebug(__METHOD__ . '|' . (string)$th); - } - } - - /** - * Check if is a failed transaction - * - * @return boolean - */ - protected function isFailedGroupTransaction(): bool - { - return $this->pushRequst->hasPostData( - 'transaction_type', - self::BUCK_PUSH_GROUPTRANSACTION_TYPE - ) && $this->pushRequst->hasPostData( - 'statuscode', - $this->helper->getStatusCode('BUCKAROO_MAGENTO2_STATUSCODE_FAILED') - ); - } - - /** - * Ship push handling for a failed transaction - * - * @return bool - */ - protected function skipHandlingForFailedGroupTransactions(): bool - { - return - $this->order !== null && - $this->order->getId() !== null && - $this->order->getState() == Order::STATE_CANCELED && - ( - $this->pushRequst->hasPostData( - 'brq_transaction_type', - 'V202' - ) || - - $this->pushRequst->hasPostData( - 'brq_transaction_type', - 'V203' - ) || - $this->pushRequst->hasPostData( - 'brq_transaction_type', - 'V204' - ) - ); - } - - /** - * Get quote by increment/reserved order id - * - * @param string $reservedOrderId - * @return Quote|null - */ - protected function getQuoteByReservedOrderId(string $reservedOrderId): ?Quote - { - /** @var \Magento\Quote\Model\QuoteFactory $quoteFactory */ - $quoteFactory = $this->objectManager->get('Magento\Quote\Model\QuoteFactory'); - /** @var \Magento\Quote\Model\ResourceModel\Quote $quoteResourceModel */ - $quoteResourceModel = $this->objectManager->get('Magento\Quote\Model\ResourceModel\Quote'); - - $quote = $quoteFactory->create(); - - $quoteResourceModel->load($quote, $reservedOrderId, 'reserved_order_id'); - if (!$quote->isEmpty()) { - return $quote; - } - - return null; - } - - /** - * Create order from found quote by reserved order id - * - * @param Quote $quote - * @return AbstractExtensibleModel|OrderInterface|object|null - * @throws \Exception - * @throws LocalizedException - */ - protected function createOrder(Quote $quote) - { - /** @var \Magento\Quote\Model\QuoteManagement $quoteManagement */ - $quoteManagement = $this->objectManager->get('Magento\Quote\Model\QuoteManagement'); - - return $quoteManagement->submit($quote); - } - - /** - * Cancel order for failed group transaction - * - * @param string $reservedOrderId - * @return void - * @throws LocalizedException - */ - protected function cancelOrder(string $reservedOrderId, $historyComment = 'Giftcard has expired') - { - $order = $this->order->loadByIncrementId($reservedOrderId); - - if ($order->getEntityId() === null) { - $order = $this->createOrderFromQuote($reservedOrderId); - } - - /** @var \Magento\Sales\Api\OrderManagementInterface */ - $orderManagement = $this->objectManager->get('Magento\Sales\Api\OrderManagementInterface'); - - if ($order instanceof OrderInterface && - $order->getEntityId() !== null && - $order->getState() !== Order::STATE_CANCELED - ) { - $orderManagement->cancel($order->getEntityId()); - - $order->addCommentToStatusHistory( - __($historyComment) - ) - ->setIsCustomerNotified(false) - ->setEntityName('invoice') - ->save(); - } - } - - /** - * Create order from quote - * - * @param string $reservedOrderId - * @return AbstractExtensibleModel|OrderInterface|object|null - * @throws \Exception - * @throws LocalizedException - */ - protected function createOrderFromQuote(string $reservedOrderId) - { - $quote = $this->getQuoteByReservedOrderId($reservedOrderId); - if (!$quote instanceof Quote) { - return null; - } - - //fix missing email validation - if ($quote->getCustomerEmail() == null) { - $quote->setCustomerEmail( - $quote->getBillingAddress()->getEmail() - ); - } - - $order = $this->createOrder($quote); - - //keep the quote active but remove the canceled order from it - $quote->setIsActive(true); - $quote->setOrigOrderId(0); - $quote->setReservedOrderId(null); - $quote->save(); - return $order; - } - - /** - * Cancel order when group transaction is canceled - * - * @return void - */ - public function cancelGroupTransactionOrder() - { - if( - isset($this->postData['brq_invoicenumber']) && - is_string($this->postData['brq_invoicenumber']) - ) { - $this->cancelOrder( - $this->postData['brq_invoicenumber'], - 'Inline giftcard order was canceled' - ); - } - } - - /** - * Check if the request is a canceled group transaction - * - * @return boolean - */ - public function isCanceledGroupTransaction() - { - return $this->hasPostData( - 'brq_transaction_type', - self::BUCK_PUSH_GROUPTRANSACTION_TYPE - ) && - $this->hasPostData( - 'brq_statuscode', - $this->helper->getStatusCode('BUCKAROO_MAGENTO2_STATUSCODE_CANCELLED_BY_USER') - ); + // Process Push + $pushProcessor = $this->pushProcessorsFactory->get($pushTransactionType); + return $pushProcessor->processPush($this->pushRequst); } } diff --git a/Model/Push/AfterpayProcessor.php b/Model/Push/AfterpayProcessor.php new file mode 100644 index 000000000..f03576c30 --- /dev/null +++ b/Model/Push/AfterpayProcessor.php @@ -0,0 +1,90 @@ +afterpayConfig = $afterpayConfig; + } + + /** + * @param array $paymentDetails + * @return bool + * @throws \Exception + */ + protected function invoiceShouldBeSaved(array &$paymentDetails): bool + { + if ($this->pushRequest->hasAdditionalInformation('initiated_by_magento', 1) && + ( + $this->pushRequest->hasAdditionalInformation('service_action_from_magento', 'capture') && + $this->afterpayConfig->isInvoiceCreatedAfterShipment() + )) { + $this->logging->addDebug(__METHOD__ . '|5_1|'); + $this->dontSaveOrderUponSuccessPush = true; + return false; + } + return true; + } +} \ No newline at end of file diff --git a/Model/Push/CancelAuthorizeProcessor.php b/Model/Push/CancelAuthorizeProcessor.php new file mode 100644 index 000000000..7e216c0a2 --- /dev/null +++ b/Model/Push/CancelAuthorizeProcessor.php @@ -0,0 +1,27 @@ +setTransactionKey(); + } catch (\Exception $e) { + $this->logging->addDebug($e->getLogMessage()); + } + + $this->logging->addDebug('Order autorize has been canceld, trying to update payment transactions'); + + return true; + } +} \ No newline at end of file diff --git a/Model/Push/CreditManagmentProcessor.php b/Model/Push/CreditManagmentProcessor.php new file mode 100644 index 000000000..26f5044a1 --- /dev/null +++ b/Model/Push/CreditManagmentProcessor.php @@ -0,0 +1,100 @@ +initializeFields($pushRequest); + $invoiceKey = $this->pushRequest->getInvoicekey(); + $savedInvoiceKey = $this->order->getPayment()->getAdditionalInformation('buckaroo_cm3_invoice_key'); + + if ($invoiceKey != $savedInvoiceKey) { + return false; + } + + if ($this->updateCm3InvoiceStatus()) { + $this->sendCm3ConfirmationMail(); + return true; + } + return false; + + } + + /** + * Update the Credit Management invoice status based on push request data and save invoice if required. + * + * @return bool + * @throws LocalizedException + */ + private function updateCm3InvoiceStatus(): bool + { + $isPaid = filter_var(strtolower($this->pushRequest->getIspaid()), FILTER_VALIDATE_BOOLEAN); + $canInvoice = ($this->order->canInvoice() && !$this->order->hasInvoices()); + + $amount = floatval($this->pushRequest->getAmountDebit()); + $amount = $this->order->getBaseCurrency()->formatTxt($amount); + $statusMessage = 'Payment push status : Creditmanagement invoice with a total amount of ' + . $amount . ' has been paid'; + + if (!$isPaid && !$canInvoice) { + $statusMessage = 'Payment push status : Creditmanagement invoice has been (partially) refunded'; + } + + if (!$isPaid && $canInvoice) { + $statusMessage = 'Payment push status : Waiting for consumer'; + } + + if ($isPaid && $canInvoice) { + $originalKey = BuckarooAdapter::BUCKAROO_ORIGINAL_TRANSACTION_KEY_KEY; + $this->pushRequest->setTransactions($this->order->getPayment()->getAdditionalInformation($originalKey)); + $this->pushRequest->setAmount($this->pushRequest->getAmountDebit()); + + if (!$this->saveInvoice()) { + return false; + } + } + + $this->orderRequestService->updateOrderStatus( + $this->order->getState(), + $this->order->getStatus(), + $statusMessage + ); + + return true; + } + + /** + * Sends the CM3 confirmation email if the CM3 status code is 10 and the order email has not been sent. + * + * @return void + * @throws LocalizedException + */ + private function sendCm3ConfirmationMail(): void + { + $store = $this->order->getStore(); + $cm3StatusCode = 0; + + if (!empty($this->pushRequest->getInvoicestatuscode())) { + $cm3StatusCode = $this->pushRequest->getInvoicestatuscode(); + } + + $paymentMethod = $this->order->getPayment()->getMethodInstance(); + $configOrderMail = $this->configAccount->getOrderConfirmationEmail($store) + || $paymentMethod->getConfigData('order_email', $store); + + if (!$this->order->getEmailSent() && $cm3StatusCode == 10 && $configOrderMail) { + $this->orderRequestService->sendOrderEmail($this->order); + } + } +} \ No newline at end of file diff --git a/Model/Push/DefaultProcessor.php b/Model/Push/DefaultProcessor.php new file mode 100644 index 000000000..50f242a71 --- /dev/null +++ b/Model/Push/DefaultProcessor.php @@ -0,0 +1,1052 @@ +pushTransactionType = $pushTransactionType; + $this->orderRequestService = $orderRequestService; + $this->logging = $logging; + $this->helper = $helper; + $this->transaction = $transaction; + $this->groupTransaction = $groupTransaction; + $this->buckarooStatusCode = $buckarooStatusCode; + $this->orderStatusFactory = $orderStatusFactory; + $this->configAccount = $configAccount; + } + + /** + * @throws BuckarooException + * @throws FileSystemException + * @throws \Exception + */ + public function processPush(PushRequestInterface $pushRequest): bool + { + $this->initializeFields($pushRequest); + + // Skip Push + if ($this->skipPush()) { + return true; + } + + // Check Push Dublicates + if ($this->receivePushCheckDuplicates()) { + throw new BuckarooException(__('Skipped handling this push, duplicate')); + } + + // Check if the order can be updated + if (!$this->canUpdateOrderStatus()) { + $this->logging->addDebug('Order can not receive updates'); + $this->orderRequestService->setOrderNotificationNote(__('The order has already been processed.')); + throw new BuckarooException( + __('Signature from push is correct but the order can not receive updates') + ); + } + + $this->setTransactionKey(); + + $this->setOrderStatusMessage(); + + if ((!in_array($this->payment->getMethod(), [Giftcards::CODE, Voucher::CODE])) + && $this->isGroupTransactionPart()) { + $this->savePartGroupTransaction(); + return true; + } + + if (!$this->canProcessPostData()) { + return true; + } + + if ($this->giftcardPartialPayment()) { + return true; + } + + $this->processPushByStatus(); + + $this->logging->addDebug(__METHOD__ . '|5|'); + if (!$this->dontSaveOrderUponSuccessPush) { + $this->logging->addDebug(__METHOD__ . '|5-1|'); + $this->order->save(); + } + + return true; + } + + /** + * @param PushRequestInterface $pushRequest + * @return void + * @throws \Exception + */ + protected function initializeFields(PushRequestInterface $pushRequest): void + { + $this->pushRequest = $pushRequest; + $this->order = $this->orderRequestService->getOrderByRequest(); + $this->payment = $this->order->getPayment(); + } + + /** + * Skip the push if the conditions are met. + * + * @return bool + * @throws \Exception + */ + protected function skipPush(): bool + { + // Skip Push based on specific condition + if ($this->skipSpecificTypesOfRequsts()) { + return true; + } + + if ($this->skipFirstPush()) { + throw new BuckarooException( + __('Skipped handling this push, first handle response, action will be taken on the next push.') + ); + } + + return false; + } + + /** + * @return bool + */ + protected function skipKlarnaCapture(): bool + { + if ($this->pushRequest->hasAdditionalInformation('initiated_by_magento', 1) + && $this->pushRequest->hasPostData('transaction_method', ['klarnakp', 'KlarnaKp']) + && $this->pushRequest->hasAdditionalInformation('service_action_from_magento', 'pay') + && !empty($this->pushRequest->getServiceKlarnakpCaptureid()) + ) { + return true; + } + + return false; + } + + /** + * Check if it is needed to handle the push message based on postdata + * + * @return bool + * @throws \Exception + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + protected function skipSpecificTypesOfRequsts(): bool + { + $this->logging->addDebug(__METHOD__ . '|1|'); + + $types = ['capture', 'cancelauthorize', 'cancelreservation']; + if ($this->pushRequest->hasAdditionalInformation('initiated_by_magento', 1) + && $this->pushRequest->hasAdditionalInformation('service_action_from_magento', $types) + && empty($this->pushRequest->getRelatedtransactionRefund()) + ) { + return true; + } + + return false; + } + + /** + * Buckaroo Push is send before Response, for correct flow we skip the first push + * for some payment methods + * + * @return bool + * @throws LocalizedException + */ + protected function skipFirstPush(): bool + { + $skipFirstPush = $this->payment->getAdditionalInformation('skip_push'); + $this->logging->addDebug(__METHOD__ . '|1_20|' . var_export($skipFirstPush, true)); + + + if ($skipFirstPush > 0) { + $this->payment->setAdditionalInformation('skip_push', (int)$skipFirstPush - 1); + $this->payment->save(); + return true; + } + + return false; + } + + /** + * Check for duplicate transaction pushes from Buckaroo and update the payment transaction statuses accordingly. + * + * @param int|null $receivedStatusCode + * @param string|null $trxId + * @return bool + * @throws \Exception + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + protected function receivePushCheckDuplicates(int $receivedStatusCode = null, string $trxId = null): bool + { + $save = false; + if (!$receivedStatusCode) { + $save = true; + if (empty($this->pushRequest->getStatusCode())) { + return false; + } + $receivedStatusCode = $this->pushRequest->getStatusCode(); + } + + if (!$trxId) { + if (empty($this->pushRequest->getTransactions())) { + return false; + } + $trxId = $this->pushRequest->getTransactions(); + } + + $ignoredPaymentMethods = [ + Giftcards::CODE, + Transfer::CODE + ]; + if ($this->payment + && $this->payment->getMethod() + && $receivedStatusCode + && ($this->pushTransactionType->getPushType() == PushTransactionType::BUCK_PUSH_TYPE_TRANSACTION) + && (!in_array($this->payment->getMethod(), $ignoredPaymentMethods)) + ) { + $this->logging->addDebug(__METHOD__ . '|5|'); + + $receivedTrxStatuses = $this->payment->getAdditionalInformation( + self::BUCKAROO_RECEIVED_TRANSACTIONS_STATUSES + ); + $this->logging->addDebug(__METHOD__ . '|10|' . + var_export([$receivedTrxStatuses, $receivedStatusCode], true)); + if ($receivedTrxStatuses + && is_array($receivedTrxStatuses) + && !empty($trxId) + && isset($receivedTrxStatuses[$trxId]) + && ($receivedTrxStatuses[$trxId] == $receivedStatusCode) + ) { + $orderStatus = $this->helper->getOrderStatusByState($this->order, Order::STATE_NEW); + if (($this->order->getState() == Order::STATE_NEW) + && ($this->order->getStatus() == $orderStatus) + && ($receivedStatusCode == BuckarooStatusCode::SUCCESS) + ) { + //allow duplicated pushes for 190 statuses in case if order stills to be new/pending + $this->logging->addDebug(__METHOD__ . '|13|'); + return false; + } + + $this->logging->addDebug(__METHOD__ . '|15|'); + return true; + } + if ($save) { + $this->logging->addDebug(__METHOD__ . '|17|'); + $this->setReceivedTransactionStatuses(); + $this->payment->save(); + } + } + $this->logging->addDebug(__METHOD__ . '|20|'); + + return false; + } + + /** + * It updates the BUCKAROO_RECEIVED_TRANSACTIONS_STATUSES payment additional information + * with the current received tx status. + * + * @return void + * @throws LocalizedException + */ + protected function setReceivedTransactionStatuses(): void + { + $txId = $this->pushRequest->getTransactions(); + $statusCode = $this->pushRequest->getStatusCode(); + + if (empty($txId) || empty($statusCode)) { + return; + } + + $receivedTxStatuses = $this->payment->getAdditionalInformation( + self::BUCKAROO_RECEIVED_TRANSACTIONS_STATUSES) ?? []; + $receivedTxStatuses[$txId] = $statusCode; + + $this->payment->setAdditionalInformation(self::BUCKAROO_RECEIVED_TRANSACTIONS_STATUSES, $receivedTxStatuses); + } + + /** + * Checks if the order can be updated by checking its state and status. + * + * @return bool + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + protected function canUpdateOrderStatus(): bool + { + /** + * Types of statusses + */ + $completedStateAndStatus = [Order::STATE_COMPLETE, Order::STATE_COMPLETE]; + $cancelledStateAndStatus = [Order::STATE_CANCELED, Order::STATE_CANCELED]; + $holdedStateAndStatus = [Order::STATE_HOLDED, Order::STATE_HOLDED]; + $closedStateAndStatus = [Order::STATE_CLOSED, Order::STATE_CLOSED]; + /** + * Get current state and status of order + */ + $currentStateAndStatus = [$this->order->getState(), $this->order->getStatus()]; + $this->logging->addDebug(__METHOD__ . '|1|' . var_export($currentStateAndStatus, true)); + + /** + * If the types are not the same and the order can receive an invoice the order can be udpated by BPE. + */ + if ($completedStateAndStatus != $currentStateAndStatus + && $cancelledStateAndStatus != $currentStateAndStatus + && $holdedStateAndStatus != $currentStateAndStatus + && $closedStateAndStatus != $currentStateAndStatus + ) { + return true; + } + + if (($this->order->getState() === Order::STATE_CANCELED) + && ($this->order->getStatus() === Order::STATE_CANCELED) + && ($this->pushTransactionType->getStatusKey() === 'BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS') + && $this->pushRequest->getRelatedtransactionPartialpayment() == null + ) { + $this->logging->addDebug(__METHOD__ . '|2|'); + + $this->order->setState(Order::STATE_NEW); + $this->order->setStatus('pending'); + + foreach ($this->order->getAllItems() as $item) { + $item->setQtyCanceled(0); + } + + $this->forceInvoice = true; + return true; + } + + return false; + } + + /** + * Sets the transaction key in the payment's additional information if it's not already set. + * + * @return void + */ + protected function setTransactionKey() + { + $payment = $this->order->getPayment(); + $originalKey = BuckarooAdapter::BUCKAROO_ORIGINAL_TRANSACTION_KEY_KEY; + $transactionKey = $this->getTransactionKey(); + + if (!$payment->getAdditionalInformation($originalKey) && strlen($transactionKey) > 0) { + $payment->setAdditionalInformation($originalKey, $transactionKey); + } + } + + /** + * Retrieves the transaction key from the push request. + * + * @return string + */ + protected function getTransactionKey(): string + { + $trxId = ''; + + if (!empty($this->pushRequest->getTransactions())) { + $trxId = $this->pushRequest->getTransactions(); + } + + if (!empty($this->pushRequest->getDatarequest())) { + $trxId = $this->pushRequest->getDatarequest(); + } + + if (!empty($this->pushRequest->getRelatedtransactionRefund())) { + $trxId = $this->pushRequest->getRelatedtransactionRefund(); + } + + return $trxId; + } + + /** + * @return void + */ + protected function setOrderStatusMessage(): void + { + if (!empty($this->pushRequest->getStatusmessage())) { + if ($this->order->getState() === Order::STATE_NEW + && empty($this->pushRequest->getRelatedtransactionPartialpayment()) + && $this->pushRequest->hasPostData('statuscode', BuckarooStatusCode::SUCCESS) + ) { + $this->order->setState(Order::STATE_PROCESSING); + $this->order->addStatusHistoryComment( + $this->pushRequest->getStatusmessage(), + $this->helper->getOrderStatusByState($this->order, Order::STATE_PROCESSING) + ); + } else { + $this->order->addStatusHistoryComment($this->pushRequest->getStatusmessage()); + } + } + } + + /** + * Checks if the push request is a group transaction with a non-success status code. + * + * @return false|mixed + */ + protected function isGroupTransactionPart() + { + if (!is_null($this->pushRequest->getTransactions())) { + return $this->groupTransaction->getGroupTransactionByTrxId($this->pushRequest->getTransactions()); + } + return false; + } + + /** + * Save the part group transaction. + * + * @return void + * @throws \Exception + */ + protected function savePartGroupTransaction() + { + $items = $this->groupTransaction->getGroupTransactionByTrxId($this->pushRequest->getTransactions()); + if (is_array($items) && count($items) > 0) { + foreach ($items as $item) { + $item2['status'] = $this->pushRequest->getStatusCode(); + $item2['entity_id'] = $item['entity_id']; + $this->groupTransaction->updateGroupTransaction($item2); + } + } + } + + /** + * + * @return true + */ + protected function canProcessPostData() + { + return true; + } + + /** + * Checks if the payment is a partial payment using a gift card. + * + * @return bool + * @throws LocalizedException + */ + protected function giftcardPartialPayment(): bool + { + if ($this->payment->getMethod() != Giftcards::CODE + || (!empty($this->pushRequest->getAmount()) + && $this->pushRequest->getAmount() >= $this->order->getGrandTotal()) + || empty($this->pushRequest->getRelatedtransactionPartialpayment()) + ) { + return false; + } + + if ($this->groupTransaction->isGroupTransaction($this->pushRequest->getInvoiceNumber())) { + return false; + } + + if (!$this->pushTransactionType->getTransactionType() == PushTransactionType::BUCK_PUSH_GROUPTRANSACTION_TYPE) { + $this->payment->setAdditionalInformation( + BuckarooAdapter::BUCKAROO_ORIGINAL_TRANSACTION_KEY_KEY, + $this->pushRequest->getRelatedtransactionPartialpayment() + ); + + $this->addGiftcardPartialPaymentToPaymentInformation(); + } + + return true; + } + + /** + * Adds the gift card partial payment information to the payment's additional information. + * + * @return void + */ + protected function addGiftcardPartialPaymentToPaymentInformation() + { + $payment = $this->order->getPayment(); + + $transactionAmount = $this->pushRequest->getAmount(); + $transactionKey = $this->pushRequest->getTransactions(); + $transactionMethod = $this->pushRequest->getTransactionMethod(); + + $transactionData = $payment->getAdditionalInformation(BuckarooAdapter::BUCKAROO_ALL_TRANSACTIONS); + + $transactionArray = []; + if (is_array($transactionData) && count($transactionData) > 0) { + $transactionArray = $transactionData; + } + + if (!empty($transactionKey) && $transactionAmount > 0) { + $transactionArray[$transactionKey] = [$transactionMethod, $transactionAmount]; + + $payment->setAdditionalInformation( + BuckarooAdapter::BUCKAROO_ALL_TRANSACTIONS, + $transactionArray + ); + } + } + + /** + * Process the push according the response status + * + * @return bool + * @throws LocalizedException + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + protected function processPushByStatus(): bool + { + $newStatus = $this->getNewStatus(); + $statusKey = $this->pushTransactionType->getStatusKey(); + $statusMessage = $this->pushTransactionType->getStatusMessage(); + + if ($statusKey == 'BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS') { + return $this->processSucceededPush($newStatus, $statusMessage); + } + + if (in_array($statusKey, $this->buckarooStatusCode->getFailedStatuses())) { + return $this->processFailedPush($newStatus, $statusMessage); + } + + if (in_array($statusKey, $this->buckarooStatusCode->getPendingStatuses())) { + return $this->processPendingPaymentPush(); + } + + $this->orderRequestService->setOrderNotificationNote($statusMessage); + return true; + + } + + /** + * @return false|string|null + * @throws LocalizedException + */ + protected function getNewStatus() + { + return $this->orderStatusFactory->get($this->pushRequest->getStatusCode(), $this->order); + } + + /** + * Process the successful push response from Buckaroo and update the order accordingly. + * + * @param string $newStatus + * @param string $message + * @return bool + * @throws LocalizedException + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function processSucceededPush(string $newStatus, string $message): bool + { + $this->logging->addDebug(__METHOD__ . '|1|' . var_export($newStatus, true)); + + $this->setBuckarooReservationNumber(); + + $this->sendOrderEmail(); + + $paymentDetails = $this->getPaymentDetails($message); + $paymentDetails['state'] = Order::STATE_PROCESSING; + $paymentDetails['newStatus'] = $newStatus; + + $this->dontSaveOrderUponSuccessPush = false; + + if ($this->canPushInvoice()) { + $saveInvoice = $this->invoiceShouldBeSaved($paymentDetails); + if ($saveInvoice && !$this->saveInvoice()) { + return false; + } + } + + if ($this->groupTransaction->isGroupTransaction($this->pushRequest->getInvoiceNumber())) { + $paymentDetails['forceState'] = true; + } + + $this->logging->addDebug(__METHOD__ . '|8|'); + + $this->processSucceededPushAuthorization(); + + $this->orderRequestService->updateOrderStatus( + $paymentDetails['state'], + $paymentDetails['newStatus'], + $paymentDetails['description'], + $paymentDetails['forceState'], + $this->dontSaveOrderUponSuccessPush + ); + + $this->logging->addDebug(__METHOD__ . '|9|'); + + return true; + } + + /** + * Process succeeded push authorization. + * + * @return void + */ + private function processSucceededPushAuthorization(): void + { + $authPpaymentMethods = [ + Afterpay::CODE, + Afterpay2::CODE, + Afterpay20::CODE, + Creditcard::CODE, + Klarnakp::CODE + ]; + + if (in_array($this->payment->getMethod(), $authPpaymentMethods)) { + if ((($this->payment->getMethod() == Klarnakp::CODE) + || ( + !empty($this->pushRequest->getTransactionType()) + && in_array($this->pushRequest->getTransactionType(), ['I038', 'I880']) + ) + ) && !empty($this->pushRequest->getStatusCode()) + && ($this->pushRequest->getStatusCode() == 190) + ) { + $this->logging->addDebug(__METHOD__ . '|88|' . var_export($this->payment->getMethod(), true)); + $this->order->setState(Order::STATE_PROCESSING); + $this->order->save(); + } + } + } + + protected function setBuckarooReservationNumber(): bool + { + return false; + } + + + /** + * @todo GIFTCARD PARTIAL PAYMENT TO BE MOVED in a separate class + */ + + /** + * Send Order email if was not sent + * + * @return void + * @throws LocalizedException + */ + protected function sendOrderEmail(): void + { + $store = $this->order->getStore(); + $paymentMethod = $this->payment->getMethodInstance(); + + if (!$this->order->getEmailSent() + && ($this->configAccount->getOrderConfirmationEmail($store) + || $paymentMethod->getConfigData('order_email', $store) + ) + ) { + $this->logging->addDebug(__METHOD__ . '|sendemail|' . + var_export($this->configAccount->getOrderConfirmationEmailSync($store), true)); + $this->orderRequestService->sendOrderEmail( + $this->order, + (bool)$this->configAccount->getOrderConfirmationEmailSync($store) + ); + } + } + + /** + * Can create invoice on push + * + * @return bool + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @throws LocalizedException + */ + protected function canPushInvoice(): bool + { + if ($this->payment->getMethodInstance()->getConfigData('payment_action') == 'authorize') { + return false; + } + + return true; + } + + /** + * Creates and saves the invoice and adds for each invoice the buckaroo transaction keys + * Only when the order can be invoiced and has not been invoiced before. + * + * @return bool + * @throws BuckarooException + * @throws LocalizedException + * @throws \Exception + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + protected function saveInvoice(): bool + { + $this->logging->addDebug(__METHOD__ . '|1|'); + if (!$this->forceInvoice + && (!$this->order->canInvoice() || $this->order->hasInvoices())) { + $this->logging->addDebug('Order can not be invoiced'); + return false; + } + + $this->addTransactionData(); + + $this->logging->addDebug(__METHOD__ . '|15|'); + //Fix for suspected fraud when the order currency does not match with the payment's currency + $amount = ($this->payment->isSameCurrency() + && $this->payment->isCaptureFinal($this->order->getGrandTotal())) ? + $this->order->getGrandTotal() : $this->order->getBaseTotalDue(); + $this->payment->registerCaptureNotification($amount); + $this->payment->save(); + + $transactionKey = $this->getTransactionKey(); + + if (strlen($transactionKey) <= 0) { + return true; + } + + $this->logging->addDebug(__METHOD__ . '|25|'); + + /** @var Invoice $invoice */ + foreach ($this->order->getInvoiceCollection() as $invoice) { + $invoice->setTransactionId($transactionKey)->save(); + + if (!empty($this->pushRequest->getInvoiceNumber()) + && $this->groupTransaction->isGroupTransaction($this->pushRequest->getInvoiceNumber())) { + $this->logging->addDebug(__METHOD__ . '|27|'); + $invoice->setState(2); + } + + if (!$invoice->getEmailSent() && $this->configAccount->getInvoiceEmail($this->order->getStore())) { + $this->logging->addDebug(__METHOD__ . '|30|sendinvoiceemail'); + $this->orderRequestService->sendInvoiceEmail($invoice, true); + } + } + + $this->logging->addDebug(__METHOD__ . '|35|'); + + $this->order->setIsInProcess(true); + $this->order->save(); + + $this->dontSaveOrderUponSuccessPush = true; + + return true; + } + + /** + * Adds transaction data to the order payment with the given transaction key and data. + * + * @param bool $transactionKey + * @param bool $data + * @return Payment + * @throws LocalizedException + * @throws \Exception + */ + public function addTransactionData(bool $transactionKey = false, bool $data = false): Payment + { + $this->payment = $this->order->getPayment(); + $transactionKey = $transactionKey ?: $this->getTransactionKey(); + + if (strlen($transactionKey) <= 0) { + throw new BuckarooException(__('There was no transaction ID found')); + } + + /** + * Save the transaction's response as additional info for the transaction. + */ + $postData = $data ?: $this->pushRequest->getData(); + $rawInfo = $this->helper->getTransactionAdditionalInfo($postData); + + $this->payment->setTransactionAdditionalInfo( + Transaction::RAW_DETAILS, + $rawInfo + ); + + /** + * Save the payment's transaction key. + */ + $this->payment->setTransactionId($transactionKey . '-capture'); + + $this->payment->setParentTransactionId($transactionKey); + $this->payment->setAdditionalInformation( + BuckarooAdapter::BUCKAROO_ORIGINAL_TRANSACTION_KEY_KEY, + $transactionKey + ); + + return $this->payment; + } + + /** + * Process the failed push response from Buckaroo and update the order accordingly. + * + * @param string $newStatus + * @param string $message + * @return bool + * @throws LocalizedException + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + public function processFailedPush(string $newStatus, string $message): bool + { + $this->logging->addDebug(__METHOD__ . '|1|' . var_export($newStatus, true)); + + if (($this->order->getState() === Order::STATE_PROCESSING) + && ($this->order->getStatus() === Order::STATE_PROCESSING) + ) { + //do not update to failed if we had a success already + $this->logging->addDebug(__METHOD__ . '|2|'); + return false; + } + + $description = 'Payment status : ' . $message; + + if (!empty($this->pushRequest->getServiceAntifraudAction())) { + $description .= $this->pushRequest->getServiceAntifraudAction() . + ' ' . + $this->pushRequest->getServiceAntifraudCheck() . + ' ' . + $this->pushRequest->getServiceAntifraudDetails(); + } + + $store = $this->order->getStore(); + + $buckarooCancelOnFailed = $this->configAccount->getCancelOnFailed($store); + + $payment = $this->order->getPayment(); + + if ($buckarooCancelOnFailed && $this->order->canCancel()) { + $this->logging->addDebug(__METHOD__ . '|' . 'Buckaroo push failed : ' . $message . ' : Cancel order.'); + + // BUCKM2-78: Never automatically cancelauthorize via push for afterpay + // setting parameter which will cause to stop the cancel process on + // Buckaroo/Model/Method/BuckarooAdapter.php:880 + $methods = [ + 'buckaroo_magento2_afterpay', + 'buckaroo_magento2_afterpay2', + 'buckaroo_magento2_klarna', + 'buckaroo_magento2_klarnakp' + ]; + if (in_array($payment->getMethodInstance()->getCode(), $methods)) { + $payment->setAdditionalInformation('buckaroo_failed_authorize', 1); + $payment->save(); + } + + $this->orderRequestService->updateOrderStatus(Order::STATE_CANCELED, $newStatus, $description); + + try { + $this->order->cancel()->save(); + } catch (\Throwable $t) { + $this->logging->addDebug(__METHOD__ . '|3|'); + // SignifydGateway/Gateway error on line 208" + } + return true; + } + + $this->logging->addDebug(__METHOD__ . '|4|'); + $force = false; + if (($payment->getMethodInstance()->getCode() == 'buckaroo_magento2_mrcash') + && ($this->order->getState() === Order::STATE_NEW) + && ($this->order->getStatus() === 'pending') + ) { + $force = true; + } + $this->orderRequestService->updateOrderStatus(Order::STATE_CANCELED, $newStatus, $description, $force); + + return true; + } + + protected function processPendingPaymentPush(): bool + { + return true; + } + + /** + * Get the order increment ID based on the invoice number or order number from push + * + * @return string|null + */ + protected function getOrderIncrementId(): ?string + { + $brqOrderId = null; + + if (!empty($this->pushRequest->getInvoiceNumber()) && strlen($this->pushRequest->getInvoiceNumber()) > 0) { + $brqOrderId = $this->pushRequest->getInvoiceNumber(); + } + + if (!empty($this->pushRequest->getOrderNumber()) && strlen($this->pushRequest->getOrderNumber()) > 0) { + $brqOrderId = $this->pushRequest->getOrderNumber(); + } + + return $brqOrderId; + } + + protected function getPaymentDetails($message) + { + // Set amount + $amount = $this->order->getTotalDue(); + if (!empty($this->pushRequest->getAmount())) { + $amount = floatval($this->pushRequest->getAmount()); + } + + /** + * force state eventhough this can lead to a transition of the order + * like new -> processing + */ + $forceState = false; + $this->dontSaveOrderUponSuccessPush = false; + + if ($this->canPushInvoice()) { + $description = 'Payment status : ' . $message . "
"; + $amount = $this->order->getBaseTotalDue(); + $description .= 'Total amount of ' . + $this->order->getBaseCurrency()->formatTxt($amount) . ' has been paid'; + + $this->logging->addDebug(__METHOD__ . '|4|'); + } else { + $description = 'Authorization status : ' . $message . "
"; + $description .= 'Total amount of ' . $this->order->getBaseCurrency()->formatTxt($amount) + . ' has been authorized. Please create an invoice to capture the authorized amount.'; + $forceState = true; + } + + return [ + 'amount' => $amount, + 'description' => $description, + 'forceState' => $forceState + ]; + } + + /** + * @param array $paymentDetails + * @return bool + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + protected function invoiceShouldBeSaved(array &$paymentDetails): bool + { + return true; + } +} \ No newline at end of file diff --git a/Model/Push/GroupTransactionPushProcessor.php b/Model/Push/GroupTransactionPushProcessor.php new file mode 100644 index 000000000..5661a9779 --- /dev/null +++ b/Model/Push/GroupTransactionPushProcessor.php @@ -0,0 +1,335 @@ +groupTransaction = $groupTransaction; + $this->logging = $logging; + $this->orderRequestService = $orderRequestService; + $this->orderManagement = $orderManagement; + $this->quoteManagement = $quoteManagement; + $this->quoteFactory = $quoteFactory; + $this->quoteResource = $quoteResource; + $this->defaultProcessor = $defaultProcessor; + } + + /** + * @inheritdoc + * + * @throws LocalizedException + * @throws \Exception + */ + public function processPush(PushRequestInterface $pushRequest): bool + { + $this->pushRequest = $pushRequest; + $this->order = $this->orderRequestService->getOrderByRequest($pushRequest); + + if ($this->isFailedGroupTransaction()) { + $this->handleGroupTransactionFailed(); + return true; + } + + if($this->isCanceledGroupTransaction()) { + $this->cancelGroupTransactionOrder(); + return true; + } + + // Check if is group transaction info + if ($this->isGroupTransactionInfo()) { + return true; + } + + // Skip Handle group transaction + if ($this->skipHandlingForFailedGroupTransactions()) { + return true; + } + + return $this->defaultProcessor->processPush($pushRequest); + } + + /** + * Check if is a failed transaction + * + * @return boolean + */ + protected function isFailedGroupTransaction(): bool + { + return $this->pushRequest->hasPostData('statuscode', BuckarooStatusCode::FAILED); + } + + /** + * Checks if the group transaction is an info transaction + * + * @return bool + */ + private function isGroupTransactionInfo() + { + return $this->pushRequest->getStatusCode() != BuckarooStatusCode::SUCCESS; + } + + /** + * Check if the request is a canceled group transaction + * + * @return boolean + */ + public function isCanceledGroupTransaction() + { + return $this->pushRequest->hasPostData('brq_statuscode', BuckarooStatusCode::CANCELLED_BY_USER); + } + + /** + * Handle push from main group transaction fail + * + * @return void + */ + protected function handleGroupTransactionFailed() + { + try { + $this->cancelOrder($this->pushRequest->getInvoiceNumber()); + $this->groupTransaction->setGroupTransactionsStatus( + $this->pushRequest->getTransactions(), + $this->pushRequest->getStatusCode() + ); + + $this->savePartGroupTransaction(); + } catch (\Throwable $th) { + $this->logging->addDebug(__METHOD__ . '|' . (string)$th); + } + } + + /** + * Cancel order when group transaction is canceled + * + * @return void + * @throws LocalizedException + */ + public function cancelGroupTransactionOrder(): void + { + if (is_string($this->pushRequest->getInvoiceNumber())) { + $this->cancelOrder( + $this->pushRequest->getInvoiceNumber(), + 'Inline giftcard order was canceled' + ); + } + } + + /** + * Ship push handling for a failed transaction + * + * @return bool + */ + protected function skipHandlingForFailedGroupTransactions(): bool + { + return + $this->order->getId() !== null && + $this->order->getState() == Order::STATE_CANCELED && + $this->pushRequest->hasPostData('transaction_type', ['V202','V203', 'V204']); + } + + /** + * Cancel order for failed group transaction + * + * @param string $reservedOrderId + * @param string $historyComment + * @return void + * @throws LocalizedException + */ + protected function cancelOrder(string $reservedOrderId, string $historyComment = 'Giftcard has expired'): void + { + $order = $this->order->loadByIncrementId($reservedOrderId); + + if ($order->getEntityId() === null) { + $order = $this->createOrderFromQuote($reservedOrderId); + } + + if ($order instanceof OrderInterface && + $order->getEntityId() !== null && + $order->getState() !== Order::STATE_CANCELED + ) { + $this->orderManagement->cancel($order->getEntityId()); + + $order->addCommentToStatusHistory(__($historyComment)) + ->setIsCustomerNotified(false) + ->setEntityName('invoice') + ->save(); + } + } + + /** + * Create order from quote + * + * @param string $reservedOrderId + * @return AbstractExtensibleModel|OrderInterface|object|null + * @throws \Exception + * @throws LocalizedException + */ + protected function createOrderFromQuote(string $reservedOrderId) + { + $quote = $this->getQuoteByReservedOrderId($reservedOrderId); + if (!$quote instanceof Quote) { + return null; + } + + // fix missing email validation + if ($quote->getCustomerEmail() == null) { + $quote->setCustomerEmail( + $quote->getBillingAddress()->getEmail() + ); + } + + $order = $this->quoteManagement->submit($quote); + + // keep the quote active but remove the canceled order from it + $quote->setIsActive(true); + $quote->setOrigOrderId(0); + $quote->setReservedOrderId(null); + $quote->save(); + return $order; + } + + /** + * Get quote by increment/reserved order id + * + * @param string $reservedOrderId + * @return Quote|null + */ + protected function getQuoteByReservedOrderId(string $reservedOrderId): ?Quote + { + $quote = $this->quoteFactory->create(); + + $this->quoteResource->load($quote, $reservedOrderId, 'reserved_order_id'); + if (!$quote->isEmpty()) { + return $quote; + } + + return null; + } + + /** + * Save the part group transaction. + * + * @return void + * @throws \Exception + */ + private function savePartGroupTransaction() + { + $items = $this->groupTransaction->getGroupTransactionByTrxId($this->pushRequest->getTransactions()); + if (is_array($items) && count($items) > 0) { + foreach ($items as $item) { + $item2['status'] = $this->pushRequest->getStatusCode(); + $item2['entity_id'] = $item['entity_id']; + $this->groupTransaction->updateGroupTransaction($item2); + } + } + } +} \ No newline at end of file diff --git a/Model/Push/IdealProcessor.php b/Model/Push/IdealProcessor.php new file mode 100644 index 000000000..53a0d48d7 --- /dev/null +++ b/Model/Push/IdealProcessor.php @@ -0,0 +1,110 @@ +lockerProcess = $lockerProcess; + + } + + /** + * @throws FileSystemException + * @throws BuckarooException + */ + public function processPush(PushRequestInterface $pushRequest): bool + { + $this->pushRequest = $pushRequest; + + if ($this->lockPushProcessingCriteria()) { + $this->lockerProcess->lockProcess($this->getOrderIncrementId()); + } + + parent::processPush($pushRequest); + + $this->lockerProcess->unlockProcess(); + + return true; + } + + /** + * Determine if the lock push processing criteria are met. + * + * @return bool + */ + protected function lockPushProcessingCriteria(): bool + { + return $this->pushRequest->hasPostData('statuscode', BuckarooStatusCode::SUCCESS) + && $this->pushRequest->hasPostData('transaction_type', self::BUCK_PUSH_IDEAL_PAY); + } +} \ No newline at end of file diff --git a/Model/Push/KlarnaKpProcessor.php b/Model/Push/KlarnaKpProcessor.php new file mode 100644 index 000000000..d46401776 --- /dev/null +++ b/Model/Push/KlarnaKpProcessor.php @@ -0,0 +1,145 @@ +klarnakpConfig = $klarnakpConfig; + } + + /** + * Skip the push if the conditions are met. + * + * @return bool + * @throws \Exception + */ + protected function skipPush(): bool + { + if ($this->pushRequest->hasAdditionalInformation('initiated_by_magento', 1) + && $this->pushRequest->hasAdditionalInformation('service_action_from_magento', 'pay') + && !empty($this->pushRequest->getServiceKlarnakpCaptureid()) + ) { + return true; + } + + return parent::skipPush(); + } + + /** + * Retrieves the transaction key from the push request. + * + * @return string + */ + protected function getTransactionKey(): string + { + $trxId = parent::getTransactionKey(); + + if (!empty($this->pushRequest->getServiceKlarnakpAutopaytransactionkey()) + ) { + $trxId = $this->pushRequest->getServiceKlarnakpAutopaytransactionkey(); + } + + return $trxId; + } + + protected function setBuckarooReservationNumber(): bool + { + if (!empty($this->pushRequest->getServiceKlarnakpReservationnumber())) { + $this->order->setBuckarooReservationNumber($this->pushRequest->getServiceKlarnakpReservationnumber()); + $this->order->save(); + return true; + } + + return false; + } + + /** + * @param array $paymentDetails + * @return bool + * @throws \Exception + */ + protected function invoiceShouldBeSaved(array &$paymentDetails): bool + { + if ($this->pushRequest->hasAdditionalInformation('initiated_by_magento', 1) && + ( + $this->pushRequest->hasPostData('transaction_method', 'KlarnaKp') && + $this->pushRequest->hasAdditionalInformation('service_action_from_magento', 'pay') && + empty($this->pushRequest->getServiceKlarnakpReservationnumber()) && + $this->klarnakpConfig->isInvoiceCreatedAfterShipment() + )) { + $this->logging->addDebug(__METHOD__ . '|5_1|'); + $this->dontSaveOrderUponSuccessPush = true; + return false; + } + + if (!empty($this->pushRequest->getServiceKlarnakpAutopaytransactionkey()) + && ($this->pushRequest->getStatusCode() == 190) + ) { + return true; + } + + return true; + } +} \ No newline at end of file diff --git a/Model/Push/KlarnaProcessor.php b/Model/Push/KlarnaProcessor.php new file mode 100644 index 000000000..e0a5f7072 --- /dev/null +++ b/Model/Push/KlarnaProcessor.php @@ -0,0 +1,59 @@ +pushRequest->getServiceKlarnaAutopaytransactionkey()) + ) { + $trxId = $this->pushRequest->getServiceKlarnaAutopaytransactionkey(); + } + + return $trxId; + } + + protected function setBuckarooReservationNumber(): bool + { + if (!empty($this->pushRequest->getServiceKlarnaReservationnumber())) { + $this->order->setBuckarooReservationNumber($this->pushRequest->getServiceKlarnaReservationnumber()); + $this->order->save(); + return true; + } + + return false; + } + + /** + * @param array $paymentDetails + * @return bool + * @throws \Exception + */ + protected function invoiceShouldBeSaved(array &$paymentDetails): bool + { + if (!empty($this->pushRequest->getServiceKlarnakpAutopaytransactionkey()) + && ($this->pushRequest->getStatusCode() == 190) + ) { + return true; + } + + return true; + } +} \ No newline at end of file diff --git a/Model/Push/PayPerEmailProcessor.php b/Model/Push/PayPerEmailProcessor.php new file mode 100644 index 000000000..54479bee2 --- /dev/null +++ b/Model/Push/PayPerEmailProcessor.php @@ -0,0 +1,431 @@ +lockerProcess = $lockerProcess; + $this->configPayPerEmail = $configPayPerEmail; + + } + + /** + * @throws FileSystemException + * @throws \Exception + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + public function processPush(PushRequestInterface $pushRequest): bool + { + $this->initializeFields($pushRequest); + + if ($this->lockPushProcessingCriteria()) { + $this->lockerProcess->lockProcess($this->getOrderIncrementId()); + } + + //Check if the push is PayLink request + $this->receivePushCheckPayLink(); + + // Skip Push + if ($this->skipPush()) { + return true; + } + + //Check second push for PayPerEmail + $isDifferentPaymentMethod = $this->setPaymentMethodIfDifferent(); + + // Check Push Dublicates + if ($this->receivePushCheckDuplicates()) { + throw new BuckarooException(__('Skipped handling this push, duplicate')); + } + + // Check if the order can be updated + if (!$this->canUpdateOrderStatus()) { + if ($isDifferentPaymentMethod && $this->configPayPerEmail->isEnabledB2B()) { + $this->logging->addDebug(__METHOD__ . '|$this->order->getState()|' . $this->order->getState()); + if ($this->order->getState() === Order::STATE_COMPLETE) { + $this->order->setState(Order::STATE_PROCESSING); + $this->order->save(); + } + return true; + } + $this->logging->addDebug('Order can not receive updates'); + $this->orderRequestService->setOrderNotificationNote(__('The order has already been processed.')); + throw new BuckarooException( + __('Signature from push is correct but the order can not receive updates') + ); + } + + $this->setTransactionKey(); + + $this->setOrderStatusMessage(); + + if ((!in_array($this->payment->getMethod(), [Giftcards::CODE, Voucher::CODE])) + && $this->isGroupTransactionPart()) { + $this->savePartGroupTransaction(); + return true; + } + + + if (!$this->canProcessPostData()) { + return true; + } + + if ($this->giftcardPartialPayment()) { + return true; + } + + $this->processPushByStatus(); + + $this->logging->addDebug(__METHOD__ . '|5|'); + if (!$this->dontSaveOrderUponSuccessPush) { + $this->logging->addDebug(__METHOD__ . '|5-1|'); + $this->order->save(); + } + + $this->lockerProcess->unlockProcess(); + + return true; + } + + /** + * Determine if the lock push processing criteria are met. + * + * @return bool + */ + protected function lockPushProcessingCriteria(): bool + { + return !empty($this->pushRequest->getAdditionalInformation('frompayperemail')); + } + + /** + * Set Payment method as PayPerEmail if the push request is PayLink + * + * @return void + * @throws \Exception + */ + private function receivePushCheckPayLink(): void + { + if (!empty($this->pushRequest->getAdditionalInformation('frompaylink')) + && $this->pushTransactionType->getStatusKey() == 'BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS' + ) { + $this->payment->setMethod('buckaroo_magento2_payperemail'); + $this->payment->save(); + $this->order->save(); + } + } + + /** + * Skip the push if the conditions are met. + * + * @return bool + * @throws \Exception + */ + protected function skipPush(): bool + { + if ($this->skipPayPerEmailCancel()) { + return true; + } + + // Skip Push based on specific condition + if ($this->skipSpecificTypesOfRequsts()) { + return true; + } + + if ($this->skipFirstPush()) { + throw new BuckarooException( + __('Skipped handling this push, first handle response, action will be taken on the next push.') + ); + } + + return false; + } + + /** + * Skip Process PayPerEmail cancel request if cron is enabled + * + * @return bool + */ + private function skipPayPerEmailCancel(): bool + { + $failedStatuses = $this->buckarooStatusCode->getFailedStatuses(); + if (!empty($this->pushRequest->getTransactionMethod()) + && in_array($this->pushTransactionType->getStatusKey(), $failedStatuses) + && $this->configPayPerEmail->getEnabledCronCancelPPE() + ) { + return true; + } + return false; + } + + /** + * Set the payment method if the request is from Pay Per Email + * + * @return bool + * @throws \Exception + */ + private function setPaymentMethodIfDifferent(): bool + { + $status = $this->pushTransactionType->getStatusKey(); + if (!empty($this->pushRequest->getTransactionMethod()) + && $status == 'BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS' + && $this->pushRequest->getTransactionMethod() != 'payperemail') { + + $transactionMethod = strtolower($this->pushRequest->getTransactionMethod()); + $this->payment->setAdditionalInformation('isPayPerEmail', $transactionMethod); + + $options = new \Buckaroo\Magento2\Model\Config\Source\PaymentMethods\PayPerEmail(); + foreach ($options->toOptionArray() as $item) { + if (($item['value'] == $transactionMethod) && isset($item['code'])) { + $this->payment->setMethod($item['code']); + $this->payment->setAdditionalInformation( + BuckarooAdapter::BUCKAROO_ORIGINAL_TRANSACTION_KEY_KEY, + $this->getTransactionKey() + ); + if ($item['code'] == 'buckaroo_magento2_creditcards') { + $this->payment->setAdditionalInformation('card_type', $transactionMethod); + } + } + } + $this->payment->save(); + $this->order->save(); + return true; + } + + return false; + } + + /** + * @return void + */ + protected function setOrderStatusMessage(): void + { + if (!empty($this->pushRequest->getStatusmessage())) { + if ($this->order->getState() === Order::STATE_NEW + && empty($this->pushRequest->getAdditionalInformation('frompayperemail')) + && empty($this->pushRequest->getRelatedtransactionPartialpayment()) + && $this->pushRequest->hasPostData('statuscode', BuckarooStatusCode::SUCCESS) + ) { + $this->order->setState(Order::STATE_PROCESSING); + $this->order->addStatusHistoryComment( + $this->pushRequest->getStatusmessage(), + $this->helper->getOrderStatusByState($this->order, Order::STATE_PROCESSING) + ); + } else { + $this->order->addStatusHistoryComment($this->pushRequest->getStatusmessage()); + } + } + } + + /** + * Check if the Pay Per Email payment is in B2B mode. + * + * @return bool + */ + public function isPayPerEmailB2BModePush(): bool + { + if (!isset($this->isPayPerEmailB2BModePushInitial)) { + if (!empty($this->pushRequest->getAdditionalInformation('frompayperemail')) + && !empty($this->pushRequest->getTransactionMethod()) + && ($this->pushRequest->getTransactionMethod() == 'payperemail') + && $this->configPayPerEmail->isEnabledB2B()) { + $this->logging->addDebug(__METHOD__ . '|5|'); + $this->isPayPerEmailB2BModePushInitial = true; + } + } else { + $this->isPayPerEmailB2BModePushInitial = false; + } + + return $this->isPayPerEmailB2BModePushInitial; + } + + /** + * Check if the Pay Per Email payment is in B2B mode and in the initial push. + * + * @return bool + */ + public function isPayPerEmailB2BModePushInitial(): bool + { + return $this->isPayPerEmailB2BModePush() + && ($this->pushTransactionType->getStatusKey() == 'BUCKAROO_MAGENTO2_STATUSCODE_WAITING_ON_CONSUMER'); + } + + /** + * @return false|string|null + * @throws BuckarooException + * @throws LocalizedException + */ + protected function getNewStatus() + { + $newStatus = $this->orderStatusFactory->get($this->pushRequest->getStatusCode(), $this->order); + $this->logging->addDebug(__METHOD__ . '|5|' . var_export($newStatus, true)); + + if ($this->isPayPerEmailB2BModePushInitial()) { + $this->pushTransactionType->setStatusKey('BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS'); + $newStatus = $this->configAccount->getOrderStatusSuccess(); + $this->logging->addDebug(__METHOD__ . '|15|' . var_export( + [$this->pushTransactionType->getStatusKey(), $newStatus], + true + ) + ); + } + + return $newStatus; + } + + protected function getPaymentDetails($message) + { + // Set amount + $amount = $this->order->getTotalDue(); + if (!empty($this->pushRequest->getAmount())) { + $amount = floatval($this->pushRequest->getAmount()); + } + + /** + * force state eventhough this can lead to a transition of the order + * like new -> processing + */ + $forceState = false; + $this->dontSaveOrderUponSuccessPush = false; + + if ($this->canPushInvoice()) { + $description = 'Payment status : ' . $message . "
"; + $amount = $this->order->getBaseTotalDue(); + $description .= 'Total amount of ' . + $this->order->getBaseCurrency()->formatTxt($amount) . ' has been paid'; + + $this->logging->addDebug(__METHOD__ . '|4|'); + } else { + $description = 'Authorization status : ' . $message . "
"; + $description .= 'Total amount of ' . $this->order->getBaseCurrency()->formatTxt($amount) + . ' has been authorized. Please create an invoice to capture the authorized amount.'; + $forceState = true; + } + + if ($this->isPayPerEmailB2BModePushInitial) { + $description = ''; + } + + return [ + 'amount' => $amount, + 'description' => $description, + 'forceState' => $forceState + ]; + } + + /** + * @param array $paymentDetails + * @return bool + * @throws \Exception + */ + protected function invoiceShouldBeSaved(array &$paymentDetails): bool + { + if (!$this->isPayPerEmailB2BModePushInitial && $this->isPayPerEmailB2BModePush()) { + $this->logging->addDebug(__METHOD__ . '|4_1|'); + //Fix for suspected fraud when the order currency does not match with the payment's currency + $amount = $this->payment->isSameCurrency() && $this->payment->isCaptureFinal($this->order->getGrandTotal()) + ? $this->order->getGrandTotal() + : $this->order->getBaseTotalDue(); + $this->payment->registerCaptureNotification($amount); + $this->payment->save(); + $this->order->setState('complete'); + $this->order->addStatusHistoryComment($paymentDetails['description'], 'complete'); + $this->order->save(); + + if ($transactionKey = $this->getTransactionKey()) { + foreach ($this->order->getInvoiceCollection() as $invoice) { + $invoice->setTransactionId($transactionKey)->save(); + } + } + return false; + } + return true; + } +} \ No newline at end of file diff --git a/Model/Push/PaypalProcessor.php b/Model/Push/PaypalProcessor.php new file mode 100644 index 000000000..916b5e4f0 --- /dev/null +++ b/Model/Push/PaypalProcessor.php @@ -0,0 +1,92 @@ +paypalConfig = $paypalConfig; + + } + + /** + * @return false|string|null + * @throws BuckarooException + * @throws LocalizedException + */ + protected function getNewStatus() + { + $newStatus = $this->orderStatusFactory->get($this->pushRequest->getStatusCode(), $this->order); + $this->logging->addDebug(__METHOD__ . '|5|' . var_export($newStatus, true)); + + if ($this->pushTransactionType->getStatusKey() == 'BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS' + && $this->order->getPayment()->getMethod() == PaypalConfig::CODE) { + $newSellersProtectionStatus = $this->paypalConfig->getSellersProtectionIneligible(); + if ($this->paypalConfig->getSellersProtection() && !empty($newSellersProtectionStatus)) { + $newStatus = $newSellersProtectionStatus; + } + } + + return $newStatus; + } +} \ No newline at end of file diff --git a/Model/Push/PushProcessorsFactory.php b/Model/Push/PushProcessorsFactory.php new file mode 100644 index 000000000..c5f03b23b --- /dev/null +++ b/Model/Push/PushProcessorsFactory.php @@ -0,0 +1,144 @@ +objectManager = $objectManager; + $this->pushProcessors = $pushProcessors; + $this->orderRequestService = $orderRequestService; + } + + /** + * Retrieve the appropriate push processor for a given transaction type. + * + * @param PushTransactionType|null $pushTransactionType + * @return ?PushProcessorInterface + * @throws BuckarooException + */ + public function get(?PushTransactionType $pushTransactionType): ?PushProcessorInterface + { + if (!$this->pushProcessor instanceof PushProcessorInterface) { + if (empty($this->pushProcessors)) { + throw new \LogicException('Push processors is not set.'); + } + + $pushProcessorClass = $this->getPushProcessorClass($pushTransactionType); + if (empty($pushProcessorClass)) { + throw new BuckarooException(new Phrase('Unknown Push Processor type')); + } + + $this->pushProcessor = $this->objectManager->get($pushProcessorClass); + + } + return $this->pushProcessor; + } + + /** + * Determine the class of the push processor based on the provided transaction type. + * + * @param PushTransactionType|null $pushTransactionType + * @return mixed + * @throws BuckarooException + */ + private function getPushProcessorClass(?PushTransactionType $pushTransactionType) + { + // Set Default Push Processor + $pushProcessorClass = $this->pushProcessors['default']; + + // Set Push Processor by Payment Method + $paymentMethod = $pushTransactionType->getPaymentMethod(); + $pushProcessorClass = $this->pushProcessors[$paymentMethod] ?? $pushProcessorClass; + + if ($pushTransactionType->isFromPayPerEmail()) { + return $this->pushProcessors['payperemail']; + } + + // Check if is Group Transaction Push + if ($pushTransactionType->isGroupTransaction()) { + return $this->pushProcessors['group_transaction']; + } + + // Check if is Credit Management Push + $pushType = $pushTransactionType->getPushType(); + if ($pushType == PushTransactionType::BUCK_PUSH_TYPE_INVOICE) { + return $this->pushProcessors['credit_managment']; + } + + if ($pushType == PushTransactionType::BUCK_PUSH_TYPE_INVOICE_INCOMPLETE) { + throw new BuckarooException( + __('Skipped handling this invoice push because it is too soon.') + ); + } + + // Check if is Refund or Cancel Authorize Push + if ($pushTransactionType->getServiceAction() == 'refund') { + return $this->pushProcessors['refund']; + } + + if ($pushTransactionType->getServiceAction() == 'cancel_authorize') { + return $this->pushProcessors['cancel_authorize']; + } + + return $pushProcessorClass; + } +} \ No newline at end of file diff --git a/Model/Push/PushTransactionType.php b/Model/Push/PushTransactionType.php new file mode 100644 index 000000000..0d7986b9a --- /dev/null +++ b/Model/Push/PushTransactionType.php @@ -0,0 +1,462 @@ +buckarooStatusCode = $buckarooStatusCode; + } + + /** + * @param PushRequestInterface|null $pushRequest + * @param Order|null $order + * @return PushTransactionType + */ + public function getPushTransactionType(?PushRequestInterface $pushRequest, ?Order $order): PushTransactionType + { + if (!$this->isSet) { + $this->pushRequest = $pushRequest; + $this->order = $order; + + $this->paymentMethod = $this->pushRequest->getTransactionMethod() ?? ''; + $this->pushType = $this->getPushTypeByInvoiceKey(); + $this->statusCode = $this->getStatusCodeByTransactionType($this->pushType); + $this->statusMessage = $this->buckarooStatusCode->getResponseMessage($this->statusCode); + $this->statusKey = $this->buckarooStatusCode->getStatusKey($this->statusCode); + $this->transactionType = $this->pushRequest->getTransactionType(); + $this->groupTransaction = $this->transactionType === self::BUCK_PUSH_GROUPTRANSACTION_TYPE; + $this->creditManagement = $this->pushType === self::BUCK_PUSH_TYPE_INVOICE; + $this->magentoServiceAction = $this->pushRequest->getAdditionalInformation('service_action_from_magento'); + $this->serviceAction = $this->getServiceAction(); + $this->isFromPayPerEmail = (bool)$this->pushRequest->getAdditionalInformation('frompayperemail') + ?? (bool)$this->pushRequest->getAdditionalInformation('frompayperemail') + ?? false; + + $this->isSet = true; + } + + return $this; + } + + /** + * Determine the transaction type based on push request data and the saved invoice key. + * + * @param PushRequestInterface $pushRequest + * @param Order $order + * @return string + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + public function getPushTypeByInvoiceKey(): string + { + //If an order has an invoice key, then it should only be processed by invoice pushes + $savedInvoiceKey = (string)$this->order->getPayment()->getAdditionalInformation('buckaroo_cm3_invoice_key'); + + if (!empty($this->pushRequest->getInvoicekey()) + && !empty($this->pushRequest->getSchemekey()) + && strlen($savedInvoiceKey) > 0 + ) { + return self::BUCK_PUSH_TYPE_INVOICE; + } + + if (!empty($this->pushRequest->getInvoicekey()) + && !empty($this->pushRequest->getSchemekey()) + && strlen($savedInvoiceKey) == 0 + ) { + return self::BUCK_PUSH_TYPE_INVOICE_INCOMPLETE; + } + + if (!empty($this->pushRequest->getDatarequest())) { + return self::BUCK_PUSH_TYPE_DATAREQUEST; + } + + if (empty($this->pushRequest->getInvoicekey()) + && empty($this->pushRequest->getServiceCreditmanagement3Invoicekey()) + && empty($this->pushRequest->getDatarequest()) + && strlen($savedInvoiceKey) <= 0 + ) { + return self::BUCK_PUSH_TYPE_TRANSACTION; + } + + return ''; + } + + /** + * Retrieve the status code from the push request based on the transaction type. + * + * @param string $transactionType + * @return int + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + private function getStatusCodeByTransactionType(string $transactionType): int + { + $statusCode = 0; + switch ($transactionType) { + case self::BUCK_PUSH_TYPE_TRANSACTION: + case self::BUCK_PUSH_TYPE_DATAREQUEST: + $statusCode = $this->pushRequest->getStatusCode() ?: $statusCode; + break; + case self::BUCK_PUSH_TYPE_INVOICE: + case self::BUCK_PUSH_TYPE_INVOICE_INCOMPLETE: + $statusCode = $this->pushRequest->getEventparametersStatuscode() ?: $statusCode; + $statusCode = $this->pushRequest->getEventparametersTransactionstatuscode() ?: $statusCode; + break; + } + + if ($this->pushRequest->getStatusCode() !== null + && ($this->pushRequest->getStatusCode() == BuckarooStatusCode::SUCCESS) + && !$statusCode + ) { + $statusCode = BuckarooStatusCode::SUCCESS; + } + + return (int)$statusCode; + } + + /** + * @return int + */ + public function getStatusCode(): int + { + return $this->statusCode; + } + + /** + * @param int $statusCode + */ + public function setStatusCode(int $statusCode): void + { + $this->statusCode = $statusCode; + } + + /** + * @return string + */ + public function getStatusKey(): string + { + return $this->statusKey; + } + + /** + * @param string $statusKey + */ + public function setStatusKey(string $statusKey): void + { + $this->statusKey = $statusKey; + } + + /** + * @return string|null + */ + public function getTransactionType(): ?string + { + return $this->transactionType; + } + + /** + * @param string|null $transactionType + */ + public function setTransactionType(?string $transactionType): void + { + $this->transactionType = $transactionType; + } + + /** + * @return string|null + */ + public function getServiceAction(): ?string + { + if (empty($this->serviceAction)) { + $this->serviceAction = $this->magentoServiceAction; + + if (!empty($this->pushRequest->getAmountCredit())) { + if ($this->getStatusKey() !== 'BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS' + && $this->order->isCanceled() + && $this->getTransactionType() == self::BUCK_PUSH_CANCEL_AUTHORIZE_TYPE + ) { + $this->serviceAction = 'cancel_authorize'; + } else { + $this->serviceAction = 'refund'; + } + } + } + + return $this->serviceAction; + } + + /** + * @param string $serviceAction + */ + public function setServiceAction(string $serviceAction): void + { + $this->serviceAction = $serviceAction; + } + + /** + * @return string + */ + public function getPushType(): string + { + return $this->pushType; + } + + /** + * @param string $pushType + */ + public function setPushType(string $pushType): void + { + $this->pushType = $pushType; + } + + /** + * @return string + */ + public function getPaymentMethod(): string + { + return $this->paymentMethod; + } + + /** + * @param string $paymentMethod + */ + public function setPaymentMethod(string $paymentMethod): void + { + $this->paymentMethod = $paymentMethod; + } + + /** + * @return string + */ + public function getMagentoServiceAction(): string + { + return $this->magentoServiceAction; + } + + /** + * @param string $magentoServiceAction + */ + public function setMagentoServiceAction(string $magentoServiceAction): void + { + $this->magentoServiceAction = $magentoServiceAction; + } + + /** + * @return string + */ + public function getStatusMessage(): string + { + return $this->statusMessage; + } + + /** + * @param string $statusMessage + */ + public function setStatusMessage(string $statusMessage): void + { + $this->statusMessage = $statusMessage; + } + + /** + * @return BuckarooStatusCode + */ + public function getBuckarooStatusCode(): BuckarooStatusCode + { + return $this->buckarooStatusCode; + } + + /** + * @param BuckarooStatusCode $buckarooStatusCode + */ + public function setBuckarooStatusCode(BuckarooStatusCode $buckarooStatusCode): void + { + $this->buckarooStatusCode = $buckarooStatusCode; + } + + /** + * @return bool + */ + public function isGroupTransaction(): bool + { + return $this->groupTransaction; + } + + /** + * @param bool $groupTransaction + */ + public function setGroupTransaction(bool $groupTransaction): void + { + $this->groupTransaction = $groupTransaction; + } + + /** + * @return bool + */ + public function isCreditManagment(): bool + { + return $this->creditManagement; + } + + /** + * @param bool $creditManagement + */ + public function setCreditManagment(bool $creditManagement): void + { + $this->creditManagement = $creditManagement; + } + + /** + * @return Order + */ + public function getOrder(): Order + { + return $this->order; + } + + /** + * @param Order $order + */ + public function setOrder(Order $order): void + { + $this->order = $order; + } + + /** + * @return bool + */ + public function isFromPayPerEmail(): bool + { + return $this->isFromPayPerEmail; + } + + /** + * @param Order $order + */ + public function setIsFromPayPerEmail(bool $isFromPayPerEmail): void + { + $this->isFromPayPerEmail = $isFromPayPerEmail; + } +} \ No newline at end of file diff --git a/Model/Push/RefundProcessor.php b/Model/Push/RefundProcessor.php new file mode 100644 index 000000000..ccf34a2a6 --- /dev/null +++ b/Model/Push/RefundProcessor.php @@ -0,0 +1,139 @@ +refundPush = $refundPush; + } + + /** + * @throws BuckarooException + * @throws \Exception + */ + public function processPush(PushRequestInterface $pushRequest): bool + { + $this->pushRequest = $pushRequest; + $this->order = $this->orderRequestService->getOrderByRequest($pushRequest); + $this->payment = $this->order->getPayment(); + + if ($this->skipPendingRefundPush($pushRequest)) { + return true; + } + + if ($this->pushTransactionType->getStatusKey() !== 'BUCKAROO_MAGENTO2_STATUSCODE_SUCCESS') { + if ($this->order->hasInvoices()) { + //don't proceed failed refund push + $this->logging->addDebug(__METHOD__ . '|10|'); + $this->orderRequestService->setOrderNotificationNote( + __('Push notification for refund has no success status, ignoring.') + ); + return true; + } else { + throw new BuckarooException( + __('Refund failed ! Status : %1 and the order does not contain an invoice', + $this->pushTransactionType->getStatusKey()) + ); + } + } + + return $this->refundPush->receiveRefundPush($this->pushRequest, true, $this->order); + } + + /** + * Skip Pending Refund Push + * + * @param PushRequestInterface $pushRequest + * @return bool + * @throws \Exception + */ + private function skipPendingRefundPush(PushRequestInterface $pushRequest): bool + { + if (!$pushRequest->hasAdditionalInformation('initiated_by_magento', 1) + || !$pushRequest->hasAdditionalInformation('service_action_from_magento', ['refund']) + ) { + $this->logging->addDebug(__METHOD__ . '|5|'); + return false; + } + + if ($pushRequest->hasPostData('statuscode', BuckarooStatusCode::SUCCESS) + && !empty($pushRequest->getRelatedtransactionRefund()) + && $this->receivePushCheckDuplicates( + BuckarooStatusCode::PENDING_APPROVAL, + $pushRequest->getRelatedtransactionRefund() + )) { + $this->logging->addDebug(__METHOD__ . '|4|'); + return false; + } + + + return true; + } +} \ No newline at end of file diff --git a/Model/Push/TransferProcessor.php b/Model/Push/TransferProcessor.php new file mode 100644 index 000000000..4152ee8be --- /dev/null +++ b/Model/Push/TransferProcessor.php @@ -0,0 +1,104 @@ +order->getTotalDue(); + if (!empty($this->pushRequest->getAmount())) { + $amount = floatval($this->pushRequest->getAmount()); + } + + /** + * force state eventhough this can lead to a transition of the order + * like new -> processing + */ + $forceState = false; + $this->dontSaveOrderUponSuccessPush = false; + + if ($this->canPushInvoice()) { + $description = 'Payment status : ' . $message . "
"; + if ($this->pushRequest->hasPostData('transaction_method', 'transfer')) { + //keep amount fetched from brq_amount + $description .= 'Amount of ' . $this->order->getBaseCurrency()->formatTxt($amount) . ' has been paid'; + } + $this->logging->addDebug(__METHOD__ . '|4|'); + } else { + $description = 'Authorization status : ' . $message . "
"; + $description .= 'Total amount of ' . $this->order->getBaseCurrency()->formatTxt($amount) + . ' has been authorized. Please create an invoice to capture the authorized amount.'; + $forceState = true; + } + + return [ + 'amount' => $amount, + 'description' => $description, + 'forceState' => $forceState + ]; + } + + /** + * @param array $paymentDetails + * @return bool + * @throws LocalizedException + */ + protected function invoiceShouldBeSaved(array &$paymentDetails): bool + { + $amount = $paymentDetails['amount']; + //invoice only in case of full or last remained amount + $this->logging->addDebug(__METHOD__ . '|61|' . var_export([ + $this->order->getId(), + $paymentDetails['amount'], + $this->order->getTotalDue(), + $this->order->getTotalPaid(), + ], true)); + + $saveInvoice = true; + + if (($paymentDetails['amount'] < $this->order->getTotalDue()) + || (($paymentDetails['amount'] == $this->order->getTotalDue()) && ($this->order->getTotalPaid() > 0)) + ) { + $this->logging->addDebug(__METHOD__ . '|64|'); + + $paymentDetails['forceState'] = true; + if ($amount < $this->order->getTotalDue()) { + $this->logging->addDebug(__METHOD__ . '|65|'); + $paymentDetails['state'] = Order::STATE_NEW; + $paymentDetails['newStatus'] = $this->orderStatusFactory->get( + BuckarooStatusCode::PENDING_PROCESSING, + $this->order + ); + $saveInvoice = false; + } + + $this->orderRequestService->saveAndReloadOrder(); + + $this->order->setTotalDue($this->order->getTotalDue() - $amount); + $this->order->setBaseTotalDue($this->order->getTotalDue() - $amount); + + $totalPaid = $this->order->getTotalPaid() + $amount; + $this->order->setTotalPaid( + $totalPaid > $this->order->getGrandTotal() ? $this->order->getGrandTotal() : $totalPaid + ); + + $baseTotalPaid = $this->order->getBaseTotalPaid() + $amount; + $this->order->setBaseTotalPaid( + $baseTotalPaid > $this->order->getBaseGrandTotal() ? + $this->order->getBaseGrandTotal() : $baseTotalPaid + ); + + $this->orderRequestService->saveAndReloadOrder(); + + $this->orderRequestService->updateTotalOnOrder($this->order); + } + + return $saveInvoice; + } +} \ No newline at end of file diff --git a/Model/RequestPush/AbstractPushRequest.php b/Model/RequestPush/AbstractPushRequest.php index 9a34e0b88..72484ac04 100644 --- a/Model/RequestPush/AbstractPushRequest.php +++ b/Model/RequestPush/AbstractPushRequest.php @@ -84,16 +84,15 @@ public function __call(string $methodName, array $args) */ public function hasAdditionalInformation(string $name, $value): bool { - $fieldValue = $this->getAdditionalInformation($name); /** @phpstan-ignore-line */ - if (is_array($value) && - isset($fieldValue) && - in_array($fieldValue, $value) + $fieldValue = $this->getAdditionalInformation($name); + /** @phpstan-ignore-line */ + if (is_array($value) + && isset($fieldValue) + && in_array($fieldValue, $value) ) { return true; - } - - if (isset($fieldValue) && - $fieldValue == $value + } elseif (isset($fieldValue) + && $fieldValue == $value ) { return true; } diff --git a/Model/RequestPush/HttppostPushRequest.php b/Model/RequestPush/HttppostPushRequest.php index 4ff6f7d3b..f17c48d88 100644 --- a/Model/RequestPush/HttppostPushRequest.php +++ b/Model/RequestPush/HttppostPushRequest.php @@ -32,6 +32,10 @@ * @method getSchemekey() * @method getServiceCreditmanagement3Invoicekey() * @method getEventparametersStatuscode() + * @method getEventparametersTransactionstatuscode() + * @method getIspaid() + * @method getInvoicestatuscode() + * @method getRelatedtransactionPartialpayment() */ class HttppostPushRequest extends AbstractPushRequest implements PushRequestInterface { diff --git a/Model/RequestPush/JsonPushRequest.php b/Model/RequestPush/JsonPushRequest.php index ff82a3693..4f0851f37 100644 --- a/Model/RequestPush/JsonPushRequest.php +++ b/Model/RequestPush/JsonPushRequest.php @@ -33,6 +33,10 @@ * @method getSchemekey() * @method getServiceCreditmanagement3Invoicekey() * @method getEventparametersStatuscode() + * @method getEventparametersTransactionstatuscode() + * @method getIspaid() + * @method getInvoicestatuscode() + * @method getRelatedtransactionPartialpayment() */ class JsonPushRequest extends AbstractPushRequest implements PushRequestInterface { diff --git a/Service/LockerProcess.php b/Service/LockerProcess.php new file mode 100644 index 000000000..766b32860 --- /dev/null +++ b/Service/LockerProcess.php @@ -0,0 +1,91 @@ +fileSystemDriver = $fileSystemDriver; + $this->dirList = $dirList; + } + + /** + * Lock the process + * + * @return resource|void + * @throws FileSystemException + */ + public function lockProcess($lockName) + { + if ($this->lockFilePath = $this->getLockProcessingFilePath($lockName)) { + if ($fp = $this->fileSystemDriver->fileOpen($this->lockFilePath, "w+")) { + $this->fileSystemDriver->fileLock($fp); + return $fp; + } + } + } + + /** + * Unlock the process. + * + * @throws FileSystemException + */ + public function unlockProcess(): void + { + if ($this->lockFilePath && $this->fileSystemDriver->isExists($this->lockFilePath)) { + $this->fileSystemDriver->deleteFile($this->lockFilePath); + } + } + + /** + * Get the file path for the lock push processing file. + * + * @param string $lockName + * @return string + * @throws FileSystemException + */ + private function getLockProcessingFilePath(string $lockName): string + { + return $this->dirList->getPath('tmp') . DIRECTORY_SEPARATOR . 'bk_push_ppe_' . sha1($lockName); + } +} \ No newline at end of file diff --git a/Service/Push/OrderRequestService.php b/Service/Push/OrderRequestService.php new file mode 100644 index 000000000..6373e72f6 --- /dev/null +++ b/Service/Push/OrderRequestService.php @@ -0,0 +1,327 @@ +order = $order; + $this->logging = $logging; + $this->transaction = $transaction; + $this->orderSender = $orderSender; + $this->invoiceSender = $invoiceSender; + $this->resourceConnection = $resourceConnection; + } + + /** + * Load the order from the Push Data based on the Order Increment ID or transaction key. + * + * @param PushRequestInterface|null $pushRequest + * @return Order|OrderPayment + * @throws \Exception + */ + public function getOrderByRequest(?PushRequestInterface $pushRequest = null) + { + if ($this->order->getId()) { + return $this->order; + } else { + $brqOrderId = $this->getOrderIncrementIdFromRequest($pushRequest); + + $this->order->loadByIncrementId((string)$brqOrderId); + + if (!$this->order->getId()) { + $this->logging->addDebug('Order could not be loaded by Invoice Number or Order Number'); + // Try to get order by transaction id on payment. + $this->order = $this->getOrderByTransactionKey($pushRequest); + } + } + + return $this->order; + } + + /** + * Get the order increment ID based on the invoice number or order number. + * + * @param $pushRequest + * @return string|null + */ + protected function getOrderIncrementIdFromRequest($pushRequest): ?string + { + $brqOrderId = null; + + if (!empty($pushRequest->getInvoiceNumber()) && strlen($pushRequest->getInvoiceNumber()) > 0) { + $brqOrderId = $pushRequest->getInvoiceNumber(); + } + + if (!empty($pushRequest->getOrderNumber()) && strlen($pushRequest->getOrderNumber()) > 0) { + $brqOrderId = $pushRequest->getOrderNumber(); + } + + return $brqOrderId; + } + + /** + * Sometimes the push does not contain the order id, when that's the case try to get the order by his payment, + * by using its own transaction key. + * + * @param $pushRequest + * @return OrderPayment + * @throws \Exception + */ + protected function getOrderByTransactionKey($pushRequest): OrderPayment + { + $trxId = $this->getTransactionKey($pushRequest); + + $this->transaction->load($trxId, 'txn_id'); + $order = $this->transaction->getOrder(); + + if (!$order) { + throw new BuckarooException(__('There was no order found by transaction Id')); + } + + return $order; + } + + /** + * Retrieves the transaction key from the push request. + * + * @param $pushRequest + * @return string + */ + protected function getTransactionKey($pushRequest): string + { + $trxId = ''; + + if (!empty($pushRequest->getTransactions())) { + $trxId = $pushRequest->getTransactions(); + } + + if (!empty($pushRequest->getDatarequest())) { + $trxId = $pushRequest->getDatarequest(); + } + + if (!empty($pushRequest->getRelatedtransactionRefund())) { + $trxId = $pushRequest->getRelatedtransactionRefund(); + } + + return $trxId; + } + + /** + * Try to add a notification note to the order comments. + * + * @param Phrase|string $message + */ + public function setOrderNotificationNote($message): void + { + $note = 'Buckaroo attempted to update this order, but failed: ' . $message; + try { + $this->order->addStatusToHistory($note); + $this->order->save(); + } catch (\Exception $e) { + $this->logging->addDebug($e->getLogMessage()); + } + } + + /** + * Updates the order state and add a comment. + * + * @param string $orderState + * @param string $newStatus + * @param string $description + * @param bool $force + * @param bool $dontSaveOrderUponSuccessPush + * + * @throws \Exception + */ + public function updateOrderStatus( + string $orderState, + string $newStatus, + string $description, + bool $force = false, + bool $dontSaveOrderUponSuccessPush = false + ): void { + $this->logging->addDebug(__METHOD__ . '|0|' . var_export([$orderState, $newStatus, $description], true)); + if ($this->order->getState() == $orderState || $force) { + $this->logging->addDebug(__METHOD__ . '|1|'); + $this->logging->addDebug('||| $orderState: ' . '|1|' . $orderState); + if ($dontSaveOrderUponSuccessPush) { + $this->order->addCommentToStatusHistory($description) + ->setIsCustomerNotified(false) + ->setEntityName('invoice') + ->setStatus($newStatus) + ->save(); + } else { + $this->order->addCommentToStatusHistory($description, $newStatus); + } + } else { + $this->logging->addDebug(__METHOD__ . '|2|'); + $this->logging->addDebug('||| $orderState: ' . '|2|' . $orderState); + if ($dontSaveOrderUponSuccessPush) { + $this->order->addCommentToStatusHistory($description) + ->setIsCustomerNotified(false) + ->setEntityName('invoice') + ->save(); + } else { + $this->order->addCommentToStatusHistory($description); + } + } + } + + /** + * Sends order email to the customer. + * + * @param Order $order + * @param bool $forceSyncMode + * @return bool + */ + public function sendOrderEmail(Order $order, bool $forceSyncMode = false): bool + { + return $this->orderSender->send($order, $forceSyncMode); + } + + /** + * Sends order invoice email to the customer. + * + * @param Invoice $invoice + * @param bool $forceSyncMode + * @return bool + * + * @throws \Exception + */ + public function sendInvoiceEmail(Invoice $invoice, bool $forceSyncMode = false): bool + { + return $this->invoiceSender->send($invoice, $forceSyncMode); + } + + public function updateTotalOnOrder($order) { + + try { + $connection = $this->resourceConnection->getConnection(); + $connection->update( + $connection->getTableName('sales_order'), + [ + 'total_due' => $order->getTotalDue(), + 'base_total_due' => $order->getTotalDue(), + 'total_paid' => $order->getTotalPaid(), + 'base_total_paid' => $order->getBaseTotalPaid(), + ], + $connection->quoteInto('entity_id = ?', $order->getId()) + ); + + return true; + } catch (\Exception $exception) { + return false; + } + + } + + /** + * Save the current order and reload it from the database. + * + * @return void + * @throws \Exception + */ + public function saveAndReloadOrder() + { + $this->order->save(); + $this->loadOrder(); + } + + /** + * Load the order from the Push Data based on the Order Increment ID or transaction key. + * + * @return void + * @throws \Exception + */ + public function loadOrder() + { + $brqOrderId = $this->getOrderIncrementId(); + + //Check if the order can receive further status updates + $this->order->loadByIncrementId((string)$brqOrderId); + + if (!$this->order->getId()) { + $this->logging->addDebug('Order could not be loaded by Invoice Number or Order Number'); + // try to get order by transaction id on payment. + $this->order = $this->getOrderByTransactionKey(); + } + } +} \ No newline at end of file diff --git a/etc/config.xml b/etc/config.xml index 6277b6470..c01132a96 100644 --- a/etc/config.xml +++ b/etc/config.xml @@ -502,7 +502,7 @@ 0 - PayPerEmailFacade + PayLinkFacade pending Buckaroo PayLink 0 diff --git a/etc/di.xml b/etc/di.xml index 362b5a73d..8ec1b9098 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -465,6 +465,26 @@ + + + + + Buckaroo\Magento2\Model\Push\DefaultProcessor + Buckaroo\Magento2\Model\Push\CreditManagmentProcessor + Buckaroo\Magento2\Model\Push\GroupTransactionPushProcessor + Buckaroo\Magento2\Model\Push\RefundProcessor + Buckaroo\Magento2\Model\Push\CancelAuthorizeProcessor + Buckaroo\Magento2\Model\Push\IdealProcessor + Buckaroo\Magento2\Model\Push\PaypalProcessor + Buckaroo\Magento2\Model\Push\AfterpayProcessor + Buckaroo\Magento2\Model\Push\KlarnaProcessor + Buckaroo\Magento2\Model\Push\KlarnaKpProcessor + Buckaroo\Magento2\Model\Push\PayPerEmailProcessor + Buckaroo\Magento2\Model\Push\TransferProcessor + + + + @@ -3074,6 +3094,30 @@ + + + + Buckaroo\Magento2\Model\ConfigProvider\Method\PayLink::CODE + PayLinkValueHandlerPool + + + + + + + PayLinkConfigValueHandler + + + + + + + Buckaroo\Magento2\Model\ConfigProvider\Method\PayLink + + + + +