diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index acd5af68..407be38b 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -27,7 +27,7 @@ jobs: run: composer install --no-ansi --no-interaction --no-progress --no-scripts - name: Run PHPStan - run: ./vendor/bin/phpstan + run: ./vendor/bin/phpstan -v php-cs: name: PHP CS diff --git a/Api/Data/ProcessOrderResultInterface.php b/Api/Data/ProcessOrderResultInterface.php index d2d727b6..adaab245 100644 --- a/Api/Data/ProcessOrderResultInterface.php +++ b/Api/Data/ProcessOrderResultInterface.php @@ -64,4 +64,12 @@ public function getCustomerMessage(): ?string; * @return void */ public function setCustomerMessage(string $customerMessage): void; + + /** + * @param string|null $messageGroup + * @return void + */ + public function setSessionMessage( + ?string $messageGroup = null + ): void; } diff --git a/Block/Order/Info.php b/Block/Order/Info.php new file mode 100644 index 00000000..3ce310d9 --- /dev/null +++ b/Block/Order/Info.php @@ -0,0 +1,66 @@ +session = $session; + $this->order = $order; + parent::__construct($context, $registry, $paymentHelper, $addressRenderer, $data); + } + + /** + * @inheritDoc + * @return void + * @throws LocalizedException + */ + protected function _prepareLayout(): void + { + $orderId = $this->getOrder()->getRealOrderId(); + $payment = $this->getOrder()->getPayment(); + if (!$orderId) { + $orderId = $this->session->getLastRealOrderId(); + } + if (!$payment) { + $order = $this->order->loadByIncrementId($orderId); + $payment = $order->getPayment(); + } + $this->pageConfig->getTitle()->set(__('Order # %1', $orderId)); + $infoBlock = $this->paymentHelper->getInfoBlock($payment, $this->getLayout()); + $this->setChild('payment_info', $infoBlock); + } +} diff --git a/Block/Order/View.php b/Block/Order/View.php new file mode 100644 index 00000000..8bc6f477 --- /dev/null +++ b/Block/Order/View.php @@ -0,0 +1,66 @@ +session = $session; + $this->order = $order; + parent::__construct($context, $registry, $httpContext, $paymentHelper, $data); + } + + /** + * @inheritDoc + * @return void + * @throws LocalizedException + */ + protected function _prepareLayout(): void + { + $orderId = $this->getOrder()->getRealOrderId(); + $payment = $this->getOrder()->getPayment(); + if (!$orderId) { + $orderId = $this->session->getLastRealOrderId(); + } + if (!$payment) { + $order = $this->order->loadByIncrementId($orderId); + $payment = $order->getPayment(); + } + $this->pageConfig->getTitle()->set(__('Order # %1', $orderId)); + $infoBlock = $this->_paymentHelper->getInfoBlock($payment, $this->getLayout()); + $this->setChild('payment_info', $infoBlock); + } +} diff --git a/Controller/CardPayments/Confirm.php b/Controller/CardPayments/Confirm.php index 32ba8ab6..ea4c5eb1 100644 --- a/Controller/CardPayments/Confirm.php +++ b/Controller/CardPayments/Confirm.php @@ -10,7 +10,9 @@ use Magento\Framework\App\RequestInterface; use Magento\Framework\Controller\ResultFactory; use Magento\Framework\Data\Form\FormKey\Validator; +use Magento\Framework\Session\SessionManagerInterface; use Magento\Sales\Api\OrderRepositoryInterface; +use Rvvup\Payments\Gateway\Method; use Rvvup\Payments\Model\SdkProxy; use Rvvup\Sdk\Exceptions\ApiError; @@ -32,28 +34,36 @@ class Confirm implements HttpPostActionInterface, CsrfAwareActionInterface */ private $orderRepository; - /** @var Validator */ + /** @var Validator */ private $formKeyValidator; + /** + * @var SessionManagerInterface + */ + private $checkoutSession; + /** * @param ResultFactory $resultFactory * @param SdkProxy $sdkProxy * @param RequestInterface $request * @param OrderRepositoryInterface $orderRepository * @param Validator $formKeyValidator + * @param SessionManagerInterface $checkoutSession $ */ public function __construct( ResultFactory $resultFactory, SdkProxy $sdkProxy, RequestInterface $request, OrderRepositoryInterface $orderRepository, - Validator $formKeyValidator + Validator $formKeyValidator, + SessionManagerInterface $checkoutSession ) { $this->resultFactory = $resultFactory; $this->sdkProxy = $sdkProxy; $this->request = $request; $this->orderRepository = $orderRepository; $this->formKeyValidator = $formKeyValidator; + $this->checkoutSession = $checkoutSession; } public function execute() @@ -61,38 +71,27 @@ public function execute() $response = $this->resultFactory->create($this->resultFactory::TYPE_JSON); try { - $orderId = $this->request->getParam('order_id', false); - $order = $this->orderRepository->get((int)$orderId); - - if ($order) { - $rvvupOrderId = (string) $order->getPayment()->getAdditionalInformation('rvvup_order_id'); - $rvvupOrder = $this->sdkProxy->getOrder($rvvupOrderId); - $rvvupPaymentId = $rvvupOrder['payments'][0]['id']; - - $authorizationResponse = $this->request->getParam('auth', false); - $threeDSecureResponse = $this->request->getParam('three_d', null); - - $this->sdkProxy->confirmCardAuthorization( - $rvvupPaymentId, - $rvvupOrderId, - $authorizationResponse, - $threeDSecureResponse - ); - - $response->setData([ - 'success' => true, - ]); - } else { - $response->setData([ - 'success' => false, - 'error_message' => 'Order not found during card authorization', - 'retryable' => false, - ]); - } + $quote = $this->checkoutSession->getQuote(); + $rvvupOrderId = (string)$quote->getPayment()->getAdditionalInformation('transaction_id'); + $rvvupPaymentId = $quote->getPayment()->getAdditionalInformation(Method::PAYMENT_ID); + + $authorizationResponse = $this->request->getParam('auth', false); + $threeDSecureResponse = $this->request->getParam('three_d'); + + $this->sdkProxy->confirmCardAuthorization( + $rvvupPaymentId, + $rvvupOrderId, + $authorizationResponse, + $threeDSecureResponse + ); + + $response->setData([ + 'success' => true, + ]); + $response->setHttpResponseCode(200); return $response; } catch (\Exception $exception) { - $data = [ 'success' => false, 'error_message' => $exception->getMessage() diff --git a/Controller/Express/Cancel.php b/Controller/Express/Cancel.php index 7f1a5f1d..ef07c127 100644 --- a/Controller/Express/Cancel.php +++ b/Controller/Express/Cancel.php @@ -5,6 +5,7 @@ use Magento\Checkout\Model\Session; use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\Controller\ResultFactory; +use Rvvup\Payments\Gateway\Method; use Rvvup\Payments\Model\SdkProxy; class Cancel implements HttpGetActionInterface @@ -39,16 +40,14 @@ public function __construct( public function execute() { $payment = $this->checkoutSession->getQuote()->getPayment(); - if ($payment->getAdditionalInformation('is_rvvup_express_payment')) { - $rvvupOrderId = $payment->getAdditionalInformation('rvvup_order_id'); - $order = $this->sdkProxy->getOrder($rvvupOrderId); - if ($order && isset($order['payments'])) { - $paymentId = $order['payments'][0]['id']; - $this->sdkProxy->cancelPayment($paymentId, $rvvupOrderId); - $result = $this->resultFactory->create(ResultFactory::TYPE_JSON); - $result->setData(['success' => true]); - return $result; - } + if ($payment->getAdditionalInformation(Method::EXPRESS_PAYMENT_KEY)) { + $rvvupOrderId = $payment->getAdditionalInformation(Method::ORDER_ID); + $paymentId = $payment->getAdditionalInformation(Method::PAYMENT_ID); + + $this->sdkProxy->cancelPayment($paymentId, $rvvupOrderId); + $result = $this->resultFactory->create(ResultFactory::TYPE_JSON); + $result->setData(['success' => true]); + return $result; } $result = $this->resultFactory->create(ResultFactory::TYPE_JSON); diff --git a/Controller/CardPayments/Cancel.php b/Controller/Payment/Cancel.php similarity index 73% rename from Controller/CardPayments/Cancel.php rename to Controller/Payment/Cancel.php index cfd59bfd..ba23ff5d 100644 --- a/Controller/CardPayments/Cancel.php +++ b/Controller/Payment/Cancel.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rvvup\Payments\Controller\CardPayments; +namespace Rvvup\Payments\Controller\Payment; use Magento\Checkout\Model\Session; use Magento\Framework\App\Action\HttpPostActionInterface; @@ -12,11 +12,11 @@ use Magento\Framework\Controller\ResultFactory; use Magento\Framework\Controller\ResultInterface; use Magento\Framework\Data\Form\FormKey\Validator; -use Magento\Sales\Api\Data\OrderInterface; +use Magento\Quote\Api\Data\PaymentInterface; use Magento\Sales\Api\OrderRepositoryInterface; -use Rvvup\Payments\Gateway\Method; -use Rvvup\Payments\Service\Order; use Psr\Log\LoggerInterface; +use Rvvup\Payments\Gateway\Method; +use Rvvup\Payments\Model\SdkProxy; class Cancel implements HttpPostActionInterface, CsrfAwareActionInterface { @@ -29,36 +29,36 @@ class Cancel implements HttpPostActionInterface, CsrfAwareActionInterface /** @var Session */ private $session; - /** @var Order */ - private $orderService; - /** @var LoggerInterface */ private $logger; /** @var OrderRepositoryInterface */ private $orderRepository; + /** @var SdkProxy */ + private $sdkProxy; + /** * @param ResultFactory $resultFactory * @param Validator $formKeyValidator * @param Session $session - * @param Order $orderService * @param OrderRepositoryInterface $orderRepository + * @param SdkProxy $sdkProxy * @param LoggerInterface $logger */ public function __construct( ResultFactory $resultFactory, Validator $formKeyValidator, Session $session, - Order $orderService, OrderRepositoryInterface $orderRepository, + SdkProxy $sdkProxy, LoggerInterface $logger ) { $this->resultFactory = $resultFactory; $this->formKeyValidator = $formKeyValidator; $this->session = $session; - $this->orderService = $orderService; $this->orderRepository = $orderRepository; + $this->sdkProxy = $sdkProxy; $this->logger = $logger; } @@ -71,11 +71,7 @@ public function execute(): ResultInterface try { if ($quote->getId()) { - $orders = $this->orderService->getAllOrdersByQuote($quote); - /** @var OrderInterface $order */ - foreach ($orders as $order) { - $this->cancelRvvupOrder($order); - } + $this->cancelRvvupOrder($quote->getPayment()); } } catch (\Exception $e) { $this->logger->warning('Rvvup order cancellation failed with message : ' . $e->getMessage()); @@ -87,20 +83,14 @@ public function execute(): ResultInterface } /** - * @param OrderInterface $order + * @param PaymentInterface $payment * @return void */ - private function cancelRvvupOrder(OrderInterface $order): void + private function cancelRvvupOrder(PaymentInterface $payment): void { - $payment = $order->getPayment(); - if ($payment->getMethod()) { - if (strpos($payment->getMethod(), Method::PAYMENT_TITLE_PREFIX) === 0) { - if ($order->canCancel()) { - $order->cancel(); - $this->orderRepository->save($order); - } - } - } + $rvvupOrderId = $payment->getAdditionalInformation(Method::ORDER_ID); + $paymentId = $payment->getAdditionalInformation(Method::PAYMENT_ID); + $this->sdkProxy->cancelPayment($paymentId, $rvvupOrderId); } /** diff --git a/Controller/Redirect/Cancel.php b/Controller/Redirect/Cancel.php deleted file mode 100644 index 5fd4d35b..00000000 --- a/Controller/Redirect/Cancel.php +++ /dev/null @@ -1,149 +0,0 @@ -resultFactory = $resultFactory; - $this->checkoutSession = $checkoutSession; - $this->messageManager = $messageManager; - $this->paymentDataGet = $paymentDataGet; - $this->processorPool = $processorPool; - $this->logger = $logger; - } - - /** - * @return \Magento\Framework\Controller\ResultInterface|\Magento\Framework\App\ResponseInterface - */ - public function execute() - { - $order = $this->checkoutSession->getLastRealOrder(); - - try { - /** @var \Magento\Framework\Controller\Result\Redirect $redirect */ - $redirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); - - $params = ['_secure' => true]; - - $rvvupData = $this->paymentDataGet->execute( - $order->getPayment() !== null && $order->getPayment()->getCcTransId() !== null - ? $order->getPayment()->getCcTransId() - : '' - ); - - $result = $this->processorPool->getProcessor($rvvupData['payments'][0]['status'] ?? '') - ->execute($order, $rvvupData); - - // Restore quote if the result would be of type error. - if ($result->getResultType() === ProcessOrderResultInterface::RESULT_TYPE_ERROR) { - $this->checkoutSession->restoreQuote(); - } - - // If specifically we are redirecting the user to the checkout page, - // set the redirect to the payment step - // and set the messages to be added to the custom group. - if ($result->getRedirectPath() === In::FAILURE) { - $params['_fragment'] = 'payment'; - $messageGroup = SessionMessageInterface::MESSAGE_GROUP; - } - - $this->setSessionMessage($result, $messageGroup ?? null); - - $redirect->setPath($result->getRedirectPath(), $params); - } catch (Exception $e) { - $this->messageManager->addErrorMessage(__('An error occurred while processing your payment.')); - $this->logger->error($e->getMessage()); - $redirect->setPath(In::ERROR, ['_secure' => true]); - } - return $redirect; - } - - /** - * Set the session message in the message container. - * - * Only handle success & error messages. - * Default to Warning container if none of the above - * Allow custom message group for the checkout page specifically. - * - * @param \Rvvup\Payments\Api\Data\ProcessOrderResultInterface $processOrderResult - * @param string|null $messageGroup - * @return void - */ - private function setSessionMessage( - ProcessOrderResultInterface $processOrderResult, - ?string $messageGroup = null - ): void { - // If no message to display, no action. - if ($processOrderResult->getCustomerMessage() === null) { - return; - } - - switch ($processOrderResult->getResultType()) { - case ProcessOrderResultInterface::RESULT_TYPE_SUCCESS: - $this->messageManager->addSuccessMessage(__($processOrderResult->getCustomerMessage()), $messageGroup); - break; - case ProcessOrderResultInterface::RESULT_TYPE_ERROR: - $this->messageManager->addErrorMessage(__($processOrderResult->getCustomerMessage()), $messageGroup); - break; - default: - $this->messageManager->addWarningMessage(__($processOrderResult->getCustomerMessage()), $messageGroup); - } - } -} diff --git a/Controller/Redirect/In.php b/Controller/Redirect/In.php index 30ab3899..8e71a4c9 100644 --- a/Controller/Redirect/In.php +++ b/Controller/Redirect/In.php @@ -1,23 +1,21 @@ -request = $request; $this->resultFactory = $resultFactory; $this->checkoutSession = $checkoutSession; $this->messageManager = $messageManager; - $this->paymentDataGet = $paymentDataGet; - $this->processorPool = $processorPool; $this->logger = $logger; - $this->orderService = $orderService; + $this->orderRepository = $orderRepository; + $this->captureService = $captureService; } /** - * @return \Magento\Framework\Controller\ResultInterface|\Magento\Framework\Controller\Result\Redirect + * @return ResultInterface|Redirect + * @throws LocalizedException */ public function execute() { $rvvupId = $this->request->getParam('rvvup-order-id'); + $quote = $this->checkoutSession->getQuote(); - // First validate we have a Rvvup Order ID, silently return to basket page. - // A standard Rvvup return should always include `rvvup-order-id` param. - if ($rvvupId === null) { - $this->logger->error('No Rvvup Order ID provided'); - - return $this->redirectToCart(); + if (!$quote->getId()) { + $quote = $this->captureService->getQuoteByRvvupId($rvvupId); } - // Get Last success order of the checkout session and validate it exists and that it has a payment. - $order = $this->checkoutSession->getLastRealOrder(); + $payment = $quote->getPayment(); + $rvvupPaymentId = $payment->getAdditionalInformation(Method::PAYMENT_ID); + $lastTransactionId = (string)$payment->getAdditionalInformation(Method::TRANSACTION_ID); + + $validate = $this->captureService->validate($rvvupId, $quote, $lastTransactionId); - /** Fix for card payments, as they don't have order in session */ - if (empty($order->getData())) { - $quote = $this->checkoutSession->getQuote(); - if (!empty($quote->getEntityId())) { - $orders = $this->orderService->getAllOrdersByQuote($quote); - $order = end($orders); - $this->checkoutSession->setData('last_real_order_id', $order->getIncrementId()); + if (!$validate['is_valid']) { + if ($validate['restore_quote']) { + $this->checkoutSession->restoreQuote(); + } + if ($validate['message']) { + $this->messageManager->addErrorMessage($validate['message']); + } + if ($validate['redirect_to_cart']) { + return $this->redirectToCart(); + } + if ($validate['already_exists']) { + if ($quote->getId()) { + $this->checkoutSession->setLastSuccessQuoteId($quote->getId()); + $this->checkoutSession->setLastQuoteId($quote->getId()); + $this->checkoutSession->setLastOrderId($quote->getReservedOrderId()); + $this->checkoutSession->setLastRealOrderId($quote->getReservedOrderId()); + return $this->captureService->processOrderResult(null, $rvvupId); + } + return $this->captureService->processOrderResult((string)$quote->getReservedOrderId(), $rvvupId, true); } } - if (!$order->getEntityId() || $order->getPayment() === null) { - $this->logger->error( - 'Could not find ' . (!$order->getEntityId() ? 'order' : 'payment') . ' for the checkout session', - [ - 'order_id' => $order->getEntityId() - ] - ); - $this->messageManager->addErrorMessage( - __( - 'An error occurred while processing your payment (ID %1). Please contact us.', - $rvvupId - ) - ); - - return $this->redirectToCart(); + $this->captureService->setCheckoutMethod($quote); + $order = $this->captureService->createOrder($rvvupId, $quote); + $orderId = $order['id']; + $reserved = $order['reserved']; + + if ($reserved) { + $this->checkoutSession->setLastSuccessQuoteId($quote->getId()); + $this->checkoutSession->setLastQuoteId($quote->getId()); + $this->checkoutSession->setLastOrderId($quote->getReservedOrderId()); + $this->checkoutSession->setLastRealOrderId($quote->getReservedOrderId()); + return $this->captureService->processOrderResult($orderId, $rvvupId, true); } - // Now validate that last payment transaction ID matches the Rvvup ID. - $lastTransactionId = $order->getPayment()->getLastTransId(); - - if ($rvvupId !== $lastTransactionId) { - $this->logger->error( - 'Payment ID when redirecting from Rvvup checkout does not match order in checkout session', - [ - 'order_id' => $order->getEntityId(), - 'payment_id' => $order->getPayment()->getEntityId(), - 'last_transaction_id' => $lastTransactionId, - 'rvvup_order_id' => $rvvupId - ] - ); + if (!$orderId) { $this->messageManager->addErrorMessage( __( - 'An error occurred while processing your payment (ID %1). Please contact us.', + 'An error occurred while creating your order (ID %1). Please contact us.', $rvvupId ) ); - + $this->checkoutSession->restoreQuote(); return $this->redirectToCart(); } - try { - // Then get the Rvvup Order by its ID. Rvvup's Redirect In action should always have the correct ID. - $rvvupData = $this->paymentDataGet->execute($rvvupId); - - if (empty($rvvupData)) { - $this->checkoutSession->restoreQuote(); - return $this->redirectToCart(); - } - - if ($rvvupData['status'] != $rvvupData['payments'][0]['status']) { - if ($rvvupData['payments'][0]['status'] !== Method::STATUS_AUTHORIZED) { - $this->processorPool->getProcessor($rvvupData['status'])->execute($order, $rvvupData); - } - } - - $result = $this->processorPool->getProcessor($rvvupData['payments'][0]['status']) - ->execute($order, $rvvupData); - - /** @var \Magento\Framework\Controller\Result\Redirect $redirect */ - $redirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); - - $params = ['_secure' => true]; - - // Restore quote if the result would be of type error. - if ($result->getResultType() === ProcessOrderResultInterface::RESULT_TYPE_ERROR - || $result->getRedirectPath() == self::ERROR) { - $this->checkoutSession->restoreQuote(); - } - - // If specifically we are redirecting the user to the checkout page, - // set the redirect to the payment step - // and set the messages to be added to the custom group. - if ($result->getRedirectPath() === self::FAILURE) { - $params['_fragment'] = 'payment'; - $messageGroup = SessionMessageInterface::MESSAGE_GROUP; - } - - $this->setSessionMessage($result, $messageGroup ?? null); - - $redirect->setPath($result->getRedirectPath(), $params); - - return $redirect; - } catch (Exception $e) { + if (!$this->captureService->paymentCapture($payment, $lastTransactionId, $rvvupPaymentId, $rvvupId)) { $this->messageManager->addErrorMessage( __( - 'An error occurred while processing your payment (ID %1). Please contact us.', + 'An error occurred while capturing your order (ID %1). Please contact us.', $rvvupId ) ); - - $this->logger->error('Error while processing Rvvup Order status with message: ' . $e->getMessage(), [ - 'order_id' => $order->getEntityId(), - 'rvvup_order_id' => $rvvupId, - 'rvvup_order_status' => $rvvupData['payments'][0]['status'] ?? '' - ]); - return $this->redirectToCart(); } + + return $this->captureService->processOrderResult($orderId, $rvvupId); } /** - * @return \Magento\Framework\Controller\ResultInterface|\Magento\Framework\Controller\Result\Redirect + * @return ResultInterface */ - private function redirectToCart() + private function redirectToCart(): ResultInterface { return $this->resultFactory->create(ResultFactory::TYPE_REDIRECT)->setPath( self::ERROR, ['_secure' => true] ); } - - /** - * Set the session message in the message container. - * - * Only handle success & error messages. - * Default to Warning container if none of the above - * Allow custom message group for the checkout page specifically. - * - * @param \Rvvup\Payments\Api\Data\ProcessOrderResultInterface $processOrderResult - * @param string|null $messageGroup - * @return void - */ - private function setSessionMessage( - ProcessOrderResultInterface $processOrderResult, - ?string $messageGroup = null - ): void { - // If no message to display, no action. - if ($processOrderResult->getCustomerMessage() === null) { - return; - } - - switch ($processOrderResult->getResultType()) { - case ProcessOrderResultInterface::RESULT_TYPE_SUCCESS: - $this->messageManager->addSuccessMessage(__($processOrderResult->getCustomerMessage()), $messageGroup); - break; - case ProcessOrderResultInterface::RESULT_TYPE_ERROR: - $this->messageManager->addErrorMessage(__($processOrderResult->getCustomerMessage()), $messageGroup); - break; - default: - $this->messageManager->addWarningMessage(__($processOrderResult->getCustomerMessage()), $messageGroup); - } - } } diff --git a/Controller/Webhook/Index.php b/Controller/Webhook/Index.php index cdfd9b8b..763dcff3 100644 --- a/Controller/Webhook/Index.php +++ b/Controller/Webhook/Index.php @@ -1,4 +1,5 @@ -request = $request; @@ -77,6 +88,7 @@ public function __construct( $this->logger = $logger; $this->publisher = $publisher; $this->webhookRepository = $webhookRepository; + $this->captureService = $captureService; $this->refundPool = $refundPool; } @@ -121,8 +133,54 @@ public function execute(): ResultInterface } elseif ($payload['event_type'] == self::PAYMENT_COMPLETED) { $webhook = $this->webhookRepository->new(['payload' => $this->serializer->serialize($payload)]); $this->webhookRepository->save($webhook); - $this->publisher->publish('rvvup.webhook', (int) $webhook->getId()); + $this->publisher->publish('rvvup.webhook', (int)$webhook->getId()); return $this->returnSuccessfulResponse(); + } elseif ($payload['event_type'] == Method::STATUS_PAYMENT_AUTHORIZED) { + $quote = $this->captureService->getQuoteByRvvupId($rvvupOrderId); + if (!$quote) { + $this->logger->debug( + 'Webhook exception: Can not find quote by rvvupId for authorize payment status', + [ + 'order_id' => $rvvupOrderId, + ] + ); + return $this->returnExceptionResponse(); + } + $payment = $quote->getPayment(); + $rvvupPaymentId = $payment->getAdditionalInformation(Method::PAYMENT_ID); + $lastTransactionId = (string)$payment->getAdditionalInformation(Method::TRANSACTION_ID); + $validate = $this->captureService->validate($rvvupOrderId, $quote, $lastTransactionId); + if (!$validate['is_valid']) { + if ($validate['redirect_to_cart']) { + return $this->returnExceptionResponse(); + } + if ($validate['already_exists']) { + return $this->returnSuccessfulResponse(); + } + } + $this->captureService->setCheckoutMethod($quote); + $order = $this->captureService->createOrder($rvvupOrderId, $quote); + $reserved = $order['reserved']; + $orderId = $order['id']; + + if ($reserved) { + return $this->returnSuccessfulResponse(); + } + + if (!$orderId) { + return $this->returnExceptionResponse(); + } + + if (!$this->captureService->paymentCapture( + $payment, + $lastTransactionId, + $rvvupPaymentId, + $rvvupOrderId + )) { + return $this->returnExceptionResponse(); + } + + $this->captureService->processOrderResult($quote->getReservedOrderId(), $rvvupOrderId, true); } return $this->returnSuccessfulResponse(); diff --git a/Exception/QuoteValidationException.php b/Exception/QuoteValidationException.php index 82b30c66..46bf98c6 100644 --- a/Exception/QuoteValidationException.php +++ b/Exception/QuoteValidationException.php @@ -6,5 +6,5 @@ class QuoteValidationException extends LocalizedException { - // + } diff --git a/Gateway/Command/CanRefund.php b/Gateway/Command/CanRefund.php index 2cbe2fa8..fe24d0d1 100644 --- a/Gateway/Command/CanRefund.php +++ b/Gateway/Command/CanRefund.php @@ -7,6 +7,7 @@ use Magento\Framework\Serialize\Serializer\Json; use Magento\Framework\Serialize\SerializerInterface; use Magento\Payment\Gateway\Config\ValueHandlerInterface; +use Magento\Sales\Block\Adminhtml\Order\Creditmemo\Create\Items; use Rvvup\Payments\Gateway\Method; use Rvvup\Payments\Model\SdkProxy; use Rvvup\Payments\Service\Cache; @@ -22,19 +23,25 @@ class CanRefund implements ValueHandlerInterface /** @var Json */ private $serializer; + /** @var Items */ + private $items; + /** * @param SdkProxy $sdkProxy * @param Cache $cache * @param Json $serializer + * @param Items $items */ public function __construct( SdkProxy $sdkProxy, Cache $cache, - Json $serializer + Json $serializer, + Items $items ) { $this->sdkProxy = $sdkProxy; $this->cache = $cache; $this->serializer = $serializer; + $this->items = $items; } /** @@ -46,7 +53,23 @@ public function handle(array $subject, $storeId = null): bool { try { $payment = $subject['payment']->getPayment(); + $orderId = $payment->getAdditionalInformation(Method::ORDER_ID); + if ($orderId) { + $invoiceCollection = $payment->getOrder()->getInvoiceCollection(); + + foreach ($invoiceCollection->getItems() as $id => $invoice) { + if (!$invoice->getTransactionId()) { + if ($this->items->getCreditmemo()->getInvoice()) { + $invoice->setTransactionId($orderId); + $invoiceCollection->removeItemByKey($id); + $invoiceCollection->addItem($invoice); + $this->items->getCreditmemo()->setInvoice($invoice); + $invoiceCollection->save(); + } + } + } + } $value = $this->cache->get($orderId, 'refund', $payment->getOrder()->getState()); if ($value) { return $this->serializer->unserialize($value)['available']; diff --git a/Gateway/Command/CreatePayment.php b/Gateway/Command/CreatePayment.php index 048acc12..94d0ad1f 100644 --- a/Gateway/Command/CreatePayment.php +++ b/Gateway/Command/CreatePayment.php @@ -1,9 +1,11 @@ -sdkProxy = $sdkProxy; $this->config = $config; + $this->paymentResource = $paymentResource; } public function execute(array $commandSubject) @@ -38,27 +48,31 @@ public function execute(array $commandSubject) $orderId = $payment->getAdditionalInformation()['rvvup_order_id']; $type = 'STANDARD'; - if ($payment->getAdditionalInformation('is_rvvup_express_payment')) { + if ($payment->getAdditionalInformation(Method::EXPRESS_PAYMENT_KEY)) { $type = 'EXPRESS'; } - $idempotencyKey = (string) time(); + $idempotencyKey = (string)time(); $data = [ 'input' => [ - 'orderId' => $orderId, - 'method' => $method, - 'type' => $type, - 'idempotencyKey' => $idempotencyKey, - 'merchantId' => $this->config->getMerchantId() + 'orderId' => $orderId, + 'method' => $method, + 'type' => $type, + 'captureType' => 'AUTOMATIC_PLUGIN', + 'idempotencyKey' => $idempotencyKey, + 'merchantId' => $this->config->getMerchantId() ] ]; if ($captureType = $payment->getMethodInstance()->getCaptureType()) { - $data['input']['captureType'] = $captureType; + if ($captureType != 'AUTOMATIC_PLUGIN' && $captureType != 'AUTOMATIC_CHECKOUT') { + $data['input']['captureType'] = $captureType; + } } - return $this->sdkProxy->createPayment( - $data - ); + $result = $this->sdkProxy->createPayment($data); + $payment->setAdditionalInformation(Method::PAYMENT_ID, $result['data']['paymentCreate']['id']); + $this->paymentResource->save($payment); + return $result; } } diff --git a/Gateway/Command/InitializeCommand.php b/Gateway/Command/InitializeCommand.php deleted file mode 100644 index 46c8df55..00000000 --- a/Gateway/Command/InitializeCommand.php +++ /dev/null @@ -1,12 +0,0 @@ -getOrder(); $rvvupOrderId = $payment->getAdditionalInformation('rvvup_order_id'); + $orderState = $payment->getOrder()->getState(); $input = $this->refundCreateInputFactory->create( $rvvupOrderId, @@ -86,7 +87,7 @@ public function execute(array $commandSubject) ); $result = $this->sdkProxy->refundCreate($input); - $this->cache->clear($rvvupOrderId); + $this->cache->clear($rvvupOrderId, $orderState); $refundId = $result['id']; $refundStatus = $result['status']; diff --git a/Gateway/CommandPool.php b/Gateway/CommandPool.php new file mode 100644 index 00000000..7e49afa2 --- /dev/null +++ b/Gateway/CommandPool.php @@ -0,0 +1,53 @@ +commands = $tmapFactory->create( + [ + 'array' => $commands, + 'type' => CommandInterface::class + ] + ); + } + + /** @inheritDoc */ + public function get($commandCode) + { + if (!isset($this->commands[$commandCode])) { + return $this; + } + + return $this->commands[$commandCode]; + } + + /** + * Mock execute command + * @param array $commandSubject + * @return void + */ + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock.DetectedFunction + public function execute(array $commandSubject) + { + } +} diff --git a/Gateway/Method.php b/Gateway/Method.php index eab45c67..28b0ebdd 100644 --- a/Gateway/Method.php +++ b/Gateway/Method.php @@ -7,6 +7,7 @@ use Magento\Payment\Gateway\Command\CommandPoolInterface; use Magento\Payment\Gateway\Config\ValueHandlerPoolInterface; use Magento\Payment\Gateway\Data\PaymentDataObjectFactory; +use Magento\Payment\Gateway\Validator\ResultInterfaceFactory; use Magento\Payment\Gateway\Validator\ValidatorPoolInterface; use Magento\Payment\Model\Method\Adapter; use Magento\Store\Model\StoreManagerInterface; @@ -24,7 +25,15 @@ class Method extends Adapter * Constant to be used as a key identifier for Rvvup payments. */ public const ORDER_ID = 'rvvup_order_id'; + public const DASHBOARD_URL = 'dashboard_url'; + + public const TRANSACTION_ID = 'transaction_id'; + + public const PAYMENT_ID = 'rvvup_payment_id'; + + public const CREATE_NEW = 'should_create_new_rvvup_order'; + public const EXPRESS_PAYMENT_KEY = 'is_rvvup_express_payment'; public const EXPRESS_PAYMENT_DATA_KEY = 'rvvup_express_payment_data'; @@ -46,6 +55,7 @@ class Method extends Adapter public const STATUS_PENDING = 'PENDING'; public const STATUS_REQUIRES_ACTION = 'REQUIRES_ACTION'; public const STATUS_AUTHORIZED = "AUTHORIZED"; + public const STATUS_PAYMENT_AUTHORIZED = "PAYMENT_AUTHORIZED"; public const STATUS_AUTHORIZATION_EXPIRED = "AUTHORIZATION_EXPIRED"; public const STATUS_SUCCEEDED = 'SUCCEEDED'; @@ -187,4 +197,10 @@ public function getCaptureType(): string { return $this->captureType; } + + /** @inheritDoc */ + public function validate() + { + return $this; + } } diff --git a/Gateway/Request/InitializeDataBuilder.php b/Gateway/Request/InitializeDataBuilder.php index 07fb6ca8..6988843b 100644 --- a/Gateway/Request/InitializeDataBuilder.php +++ b/Gateway/Request/InitializeDataBuilder.php @@ -4,10 +4,9 @@ namespace Rvvup\Payments\Gateway\Request; -use Magento\Payment\Gateway\Helper\SubjectReader; use Magento\Payment\Gateway\Request\BuilderInterface; use Magento\Payment\Model\InfoInterface; -use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\Data\CartInterface; use Psr\Log\LoggerInterface; use Rvvup\Payments\Exception\QuoteValidationException; use Rvvup\Payments\Gateway\Method; @@ -15,33 +14,22 @@ class InitializeDataBuilder implements BuilderInterface { - /** - * @var \Magento\Quote\Api\CartRepositoryInterface - */ - private $cartRepository; - /** - * @var \Rvvup\Payments\Model\OrderDataBuilder - */ + /** @var OrderDataBuilder */ private $orderDataBuilder; - /** - * @var \Psr\Log\LoggerInterface - */ + /** @var LoggerInterface */ private $logger; /** - * @param \Magento\Quote\Api\CartRepositoryInterface $cartRepository - * @param \Rvvup\Payments\Model\OrderDataBuilder $orderDataBuilder - * @param \Psr\Log\LoggerInterface $logger - * @return void + * @param OrderDataBuilder $orderDataBuilder + + * @param LoggerInterface $logger */ public function __construct( - CartRepositoryInterface $cartRepository, OrderDataBuilder $orderDataBuilder, LoggerInterface $logger ) { - $this->cartRepository = $cartRepository; $this->orderDataBuilder = $orderDataBuilder; $this->logger = $logger; } @@ -49,24 +37,15 @@ public function __construct( /** * @param array $buildSubject * @return array - * @throws \Rvvup\Payments\Exception\QuoteValidationException + * @throws QuoteValidationException * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function build(array $buildSubject): array { - $paymentDataObject = SubjectReader::readPayment($buildSubject); - - $payment = $paymentDataObject->getPayment(); - - // First check if we have an Order Payment model instance and return result if set. - $result = $this->handleOrderPayment($payment); - - if (is_array($result)) { - return $result; - } + $quote = $buildSubject['quote']; // Otherwise, we should have a Quote Payment model instance and return result if set. - $result = $this->handleQuotePayment($payment); + $result = $this->handleQuotePayment($quote); if (is_array($result)) { return $result; @@ -78,59 +57,22 @@ public function build(array $buildSubject): array throw new QuoteValidationException(__('Invalid Payment method')); } - /** - * Create the request data for an Order Payment & flag if this is a Rvvup Express Update (on order place) - * - * @param \Magento\Payment\Model\InfoInterface $payment - * @return array|null - * @throws \Rvvup\Payments\Exception\QuoteValidationException - * @throws \Magento\Framework\Exception\NoSuchEntityException - */ - private function handleOrderPayment(InfoInterface $payment): ?array - { - if (!method_exists($payment, 'getOrder')) { - return null; - } - - // Get the active quote (allow for exceptions to fall through). - $cart = $this->cartRepository->get($payment->getOrder()->getQuoteId()); - - // Build the Rvvup request data, regardless express or not. - $orderData = $this->orderDataBuilder->build($cart); - - // If this is an express payment getting completed, set payment type (express) & additional data. - if ($this->isExpressPayment($payment)) { - $orderData['id'] = $payment->getAdditionalInformation(Method::ORDER_ID); - } - - return $orderData; - } - /** * Handle initialization if this is a Quote Payment (not placed order yet). * * Currently, this is supported only for creating express payment orders. * - * @param \Magento\Payment\Model\InfoInterface $payment + * @param CartInterface $cart * @return array|null - * @throws \Rvvup\Payments\Exception\QuoteValidationException - * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws QuoteValidationException */ - private function handleQuotePayment(InfoInterface $payment): ?array + private function handleQuotePayment(CartInterface $cart): ?array { - if (!$this->isExpressPayment($payment) || !method_exists($payment, 'getQuote')) { - return null; - } - - // Get the active quote (allow for exceptions to fall through). - $cart = $this->cartRepository->getActive($payment->getQuote()->getId()); - - // Build the Rvvup request data for creating an express payment. - return $this->orderDataBuilder->build($cart, true); + return $this->orderDataBuilder->build($cart, $this->isExpressPayment($cart->getPayment())); } /** - * @param \Magento\Payment\Model\InfoInterface $payment + * @param InfoInterface $payment * @return bool */ private function isExpressPayment(InfoInterface $payment): bool diff --git a/Gateway/Response/InitializeResponseHandler.php b/Gateway/Response/InitializeResponseHandler.php index 6313f7a3..f36b97ee 100644 --- a/Gateway/Response/InitializeResponseHandler.php +++ b/Gateway/Response/InitializeResponseHandler.php @@ -6,9 +6,11 @@ use Magento\Framework\DataObject; use Magento\Framework\DataObjectFactory; +use Magento\Framework\Exception\AlreadyExistsException; use Magento\Payment\Gateway\Helper\SubjectReader; use Magento\Payment\Gateway\Response\HandlerInterface; use Magento\Payment\Model\InfoInterface; +use Magento\Quote\Model\ResourceModel\Quote\Payment; use Rvvup\Payments\Gateway\Method; class InitializeResponseHandler implements HandlerInterface @@ -18,13 +20,19 @@ class InitializeResponseHandler implements HandlerInterface */ private $dataObjectFactory; + /** @var Payment */ + private $paymentResource; + /** - * @param \Magento\Framework\DataObjectFactory $dataObjectFactory - * @return void + * @param DataObjectFactory $dataObjectFactory + * @param Payment $paymentResource */ - public function __construct(DataObjectFactory $dataObjectFactory) - { + public function __construct( + DataObjectFactory $dataObjectFactory, + Payment $paymentResource + ) { $this->dataObjectFactory = $dataObjectFactory; + $this->paymentResource = $paymentResource; } /** @@ -34,31 +42,12 @@ public function __construct(DataObjectFactory $dataObjectFactory) */ public function handle(array $handlingSubject, array $response): void { - $payment = SubjectReader::readPayment($handlingSubject)->getPayment(); - $responseDataObject = $this->getResponseDataObject($response); - - $payment = $this->setPaymentAdditionalInformation($payment, $responseDataObject); - - // If the payment method instance is not an Order Payment, no further actions. - // In that scenario, it is a Quote Payment instance for an Express Create. - if (!method_exists($payment, 'getOrder')) { - return; - } + $payment = $handlingSubject['quote']->getPayment(); - // Do not let magento set status to processing, this will be handled once back from the redirect. - $payment->setIsTransactionPending(true); - // do not close transaction, so you can do a cancel() and void - $payment->setIsTransactionClosed(false); - $payment->setShouldCloseParentTransaction(false); - - // Set the Rvvup Order ID as the transaction ID - $payment->setTransactionId($responseDataObject->getData('id')); - $payment->setCcTransId($responseDataObject->getData('id')); - $payment->setLastTransId($responseDataObject->getData('id')); + $responseDataObject = $this->getResponseDataObject($response); - // Don't send customer email. - $payment->getOrder()->setCanSendNewEmailFlag(false); + $this->setPaymentAdditionalInformation($payment, $responseDataObject); } /** @@ -71,9 +60,10 @@ public function handle(array $handlingSubject, array $response): void * @param \Magento\Payment\Model\InfoInterface $payment * @param \Magento\Framework\DataObject $responseDataObject * @return \Magento\Payment\Model\InfoInterface + * @throws AlreadyExistsException */ private function setPaymentAdditionalInformation( - InfoInterface $payment, + $payment, DataObject $responseDataObject ): InfoInterface { $payment->setAdditionalInformation(Method::ORDER_ID, $responseDataObject->getData('id')); @@ -89,24 +79,12 @@ private function setPaymentAdditionalInformation( $paymentActions = $paymentSummary['paymentActions']; } - // If this is a createOrder call for an express payment, - // then set the data to separate key. - if ($this->isExpressPayment($payment)) { - $data = [ - 'status' => $responseDataObject->getData('status'), - 'dashboardUrl' => $responseDataObject->getData('dashboardUrl'), - 'paymentActions' => $paymentActions - ]; - - $payment->setAdditionalInformation(Method::EXPRESS_PAYMENT_DATA_KEY, $data); - - return $payment; - } - // Otherwise, set normally. $payment->setAdditionalInformation('status', $responseDataObject->getData('status')); - $payment->setAdditionalInformation('dashboardUrl', $responseDataObject->getData('dashboardUrl')); + $payment->setAdditionalInformation(Method::DASHBOARD_URL, $responseDataObject->getData('dashboardUrl')); $payment->setAdditionalInformation('paymentActions', $paymentActions); + $payment->setAdditionalInformation(Method::TRANSACTION_ID, $responseDataObject->getData('id')); + $this->paymentResource->save($payment); return $payment; } diff --git a/Model/CartPaymentActionsGet.php b/Model/CartPaymentActionsGet.php index ae76e4ba..941f182e 100644 --- a/Model/CartPaymentActionsGet.php +++ b/Model/CartPaymentActionsGet.php @@ -5,9 +5,13 @@ namespace Rvvup\Payments\Model; use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NotFoundException; +use Magento\Payment\Gateway\Command\CommandException; use Magento\Payment\Gateway\Command\CommandPoolInterface; +use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Api\Data\PaymentInterface; use Magento\Quote\Api\PaymentMethodManagementInterface; +use Magento\Quote\Model\Quote; use Psr\Log\LoggerInterface; use Rvvup\Payments\Api\CartPaymentActionsGetInterface; use Rvvup\Payments\Api\Data\PaymentActionInterface; @@ -39,31 +43,60 @@ class CartPaymentActionsGet implements CartPaymentActionsGetInterface */ private $commandPool; + /** + * @var CartRepositoryInterface + */ + private $cartRepository; + /** * @param PaymentMethodManagementInterface $paymentMethodManagement * @param PaymentActionInterfaceFactory $paymentActionInterfaceFactory * @param LoggerInterface $logger + * @param CartRepositoryInterface $cartRepository * @param CommandPoolInterface $commandPool */ public function __construct( PaymentMethodManagementInterface $paymentMethodManagement, PaymentActionInterfaceFactory $paymentActionInterfaceFactory, LoggerInterface $logger, + CartRepositoryInterface $cartRepository, CommandPoolInterface $commandPool ) { $this->paymentMethodManagement = $paymentMethodManagement; $this->paymentActionInterfaceFactory = $paymentActionInterfaceFactory; $this->logger = $logger; + $this->cartRepository = $cartRepository; $this->commandPool = $commandPool; } + /** + * Get the additional information data that hold the payment actions. + * + * Get either standard or the ones saved in the express payment data field. + * @param PaymentInterface $payment + * @param Quote $cart + * @param bool $expressActions + * @return array|mixed|null + * @throws NotFoundException + * @throws CommandException + */ + private function getPaymentActions(PaymentInterface $payment, Quote $cart, bool $expressActions = false) + { + if (!$expressActions) { + return $payment->getAdditionalInformation('paymentActions'); + } + $this->commandPool->get('initialize')->execute(['quote' => $cart]); + $data = $this->commandPool->get('createPayment')->execute(['payment' => $payment]); + return $data['data']['paymentCreate']['summary']['paymentActions']; + } + /** * Get the payment actions for the specified cart ID. * * @param string $cartId * @param bool $expressActions * @return PaymentActionInterface[] - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException */ public function execute(string $cartId, bool $expressActions = false): array { @@ -72,8 +105,9 @@ public function execute(string $cartId, bool $expressActions = false): array if ($payment === null) { return []; } + $cart = $this->cartRepository->get($cartId); - $paymentActions = $this->getAdditionalInformationPaymentActions($payment, $expressActions); + $paymentActions = $this->getPaymentActions($payment, $cart, $expressActions); // Check if payment actions are set as array & not empty if (empty($paymentActions) || !is_array($paymentActions)) { @@ -112,24 +146,6 @@ public function execute(string $cartId, bool $expressActions = false): array return $paymentActionsDataArray; } - /** - * Get the additional information data that hold the payment actions. - * - * Get either standard or the ones saved in the express payment data field. - * - * @param \Magento\Quote\Api\Data\PaymentInterface $payment - * @param bool $expressActions - * @return array|mixed|null - */ - private function getAdditionalInformationPaymentActions(PaymentInterface $payment, bool $expressActions = false) - { - if (!$expressActions) { - return $payment->getAdditionalInformation('paymentActions'); - } - $data = $this->commandPool->get('createPayment')->execute(['payment' => $payment]); - return $data['data']['paymentCreate']['summary']['paymentActions']; - } - /** * Create & return a PaymentActionInterface Data object. * diff --git a/Model/Config.php b/Model/Config.php index bc8b14cb..2cbf7fbe 100644 --- a/Model/Config.php +++ b/Model/Config.php @@ -41,9 +41,10 @@ public function __construct( * Validate whether Rvvup module & payment methods are active. * * @param string $scopeType + * @param string|null $scopeCode The store's code or storeId as a string * @return bool */ - public function isActive(string $scopeType = ScopeInterface::SCOPE_STORE): bool + public function isActive(string $scopeType = ScopeInterface::SCOPE_STORE, string $scopeCode = null): bool { if (!$this->getActiveConfig($scopeType)) { return false; diff --git a/Model/ExpressPaymentCreate.php b/Model/ExpressPaymentCreate.php index b369a3d7..e16e8148 100644 --- a/Model/ExpressPaymentCreate.php +++ b/Model/ExpressPaymentCreate.php @@ -79,11 +79,7 @@ public function execute(string $cartId, string $methodCode): array throw new PaymentValidationException(__('Invalid payment method')); } - $rvvupOrderId = $payment->getAdditionalInformation(Method::ORDER_ID); - - if (!is_string($rvvupOrderId)) { - throw new PaymentValidationException(__('Invalid payment method')); - } + $payment->setAdditionalInformation(Method::CREATE_NEW, true); return $this->cartPaymentActionsGet->execute($cartId, true); } diff --git a/Model/OrderDataBuilder.php b/Model/OrderDataBuilder.php index 1a53709b..227f3573 100644 --- a/Model/OrderDataBuilder.php +++ b/Model/OrderDataBuilder.php @@ -1,4 +1,5 @@ customerAddressRepository = $customerAddressRepository; $this->urlBuilder = $urlBuilder; @@ -80,6 +73,7 @@ public function __construct( $this->cartRepository = $cartRepository; $this->orderRepository = $orderRepository; $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->paymentResource = $paymentResource; } /** @@ -115,6 +109,9 @@ public function build(CartInterface $quote, bool $express = false): array $orderDataArray['shippingAddress'] = $this->renderShippingAddress($quote, $express, $shippingAddress); + $payment = $quote->getPayment(); + $payment->setAdditionalInformation(Method::CREATE_NEW, false); + $this->paymentResource->save($payment); // As we have tangible products, the order will require shipping. $orderDataArray['shippingTotal']['amount'] = $this->toCurrency($shippingAddress->getShippingAmount()); @@ -190,7 +187,8 @@ private function renderBase(CartInterface $quote, bool $express = false): array "requiresShipping" => !$quote->getIsVirtual(), ]; - if ($payment->getAdditionalInformation("rvvup_express_payment_data")) { + if ($payment->getAdditionalInformation(Method::EXPRESS_PAYMENT_KEY) + && !$payment->getAdditionalInformation(Method::CREATE_NEW)) { unset($orderDataArray["type"]); $orderDataArray['merchantId'] = $this->config->getMerchantId(); $orderDataArray['express'] = true; @@ -202,6 +200,17 @@ private function renderBase(CartInterface $quote, bool $express = false): array $orderDataArray["items"] = $this->renderItems($quote); } + if ($id = $payment->getAdditionalInformation(Method::ORDER_ID)) { + if (!$payment->getAdditionalInformation(Method::CREATE_NEW) && + !$payment->getAdditionalInformation(Method::EXPRESS_PAYMENT_KEY) + ) { + $payment->setAdditionalInformation(Method::CREATE_NEW, true); + $this->paymentResource->save($payment); + return $orderDataArray; + } + $orderDataArray['id'] = $id; + }; + return $orderDataArray; } @@ -265,7 +274,7 @@ private function renderCustomer( // If we have an express payment and quote belongs to a customer, get customer data from customer object. if ($express && $quote->getCustomer() !== null && $quote->getCustomer()->getId() !== null) { $customerBillingAddress = $quote->getCustomer()->getDefaultBilling() !== null - ? $this->renderCustomerAddress((int) $quote->getCustomer()->getDefaultBilling()) + ? $this->renderCustomerAddress((int)$quote->getCustomer()->getDefaultBilling()) : null; return [ @@ -322,14 +331,16 @@ private function renderBillingAddress( return $billingAddress !== null ? $this->renderAddress($quote->getBillingAddress()) : null; } - // Otherwise generate the express payment create billing address from customer. - // If no default customer billing address, return null. - if ($quote->getCustomer()->getId() === null || $quote->getCustomer()->getDefaultBilling() === null) { + if ($express && $quote->getPayment()->getAdditionalInformation(Method::CREATE_NEW)) { return null; } + if ($billingAddress !== null) { + return $this->renderAddress($billingAddress); + } + // Otherwise, return customer billing address if full. - return $this->renderCustomerAddress((int) $quote->getCustomer()->getDefaultBilling()); + return $this->renderCustomerAddress((int)$quote->getCustomer()->getDefaultBilling()); } /** @@ -349,14 +360,16 @@ private function renderShippingAddress( return $shippingAddress !== null ? $this->renderAddress($quote->getShippingAddress()) : null; } - // Otherwise generate the express payment create billing address from customer. - // If no default customer billing address, return null. - if ($quote->getCustomer()->getId() === null || $quote->getCustomer()->getDefaultShipping() === null) { + if ($express && $quote->getPayment()->getAdditionalInformation(Method::CREATE_NEW)) { return null; } + if ($shippingAddress !== null) { + return $this->renderAddress($shippingAddress); + } + // Otherwise, return customer billing address if full. - return $this->renderCustomerAddress((int) $quote->getCustomer()->getDefaultShipping()); + return $this->renderCustomerAddress((int)$quote->getCustomer()->getDefaultShipping()); } /** @@ -439,7 +452,7 @@ private function renderCustomerAddress(int $customerAddressId): ?array */ private function toCurrency($amount): string { - return number_format((float) $amount, 2, '.', ''); + return number_format((float)$amount, 2, '.', ''); } /** @@ -448,7 +461,7 @@ private function toCurrency($amount): string */ private function toQty($qty): string { - return number_format((float) $qty, 0, '.', ''); + return number_format((float)$qty, 0, '.', ''); } /** diff --git a/Model/PaymentActionsGet.php b/Model/PaymentActionsGet.php index 4af74754..e97faa79 100644 --- a/Model/PaymentActionsGet.php +++ b/Model/PaymentActionsGet.php @@ -4,44 +4,34 @@ namespace Rvvup\Payments\Model; -use Exception; -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Framework\Api\SortOrderBuilder; +use Magento\Framework\Exception\AlreadyExistsException; use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NotFoundException; +use Magento\Payment\Gateway\Command\CommandException; use Magento\Payment\Gateway\Command\CommandPoolInterface; +use Magento\Quote\Api\Data\CartInterface; +use Magento\Quote\Api\Data\PaymentInterface as PaymentInterface; +use Magento\Quote\Model\ResourceModel\Quote\Payment; +use Magento\Quote\Model\QuoteRepository; use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\OrderRepositoryInterface; use Psr\Log\LoggerInterface; use Rvvup\Payments\Api\Data\PaymentActionInterface; use Rvvup\Payments\Api\Data\PaymentActionInterfaceFactory; +use Rvvup\Payments\Gateway\Method; +use Rvvup\Payments\Service\Hash; use Throwable; class PaymentActionsGet implements PaymentActionsGetInterface { /** - * @var \Magento\Framework\Api\SearchCriteriaBuilder - */ - private $searchCriteriaBuilder; - - /** - * @var \Magento\Framework\Api\SortOrderBuilder - */ - private $sortOrderBuilder; - - /** - * @var \Magento\Sales\Api\OrderRepositoryInterface - */ - private $orderRepository; - - /** - * @var \Rvvup\Payments\Api\Data\PaymentActionInterfaceFactory + * @var PaymentActionInterfaceFactory */ private $paymentActionInterfaceFactory; /** * Set via di.xml * - * @var \Psr\Log\LoggerInterface|RvvupLog + * @var LoggerInterface|RvvupLog */ private $logger; @@ -55,30 +45,39 @@ class PaymentActionsGet implements PaymentActionsGetInterface */ private $commandPool; + /** @var QuoteRepository */ + private $quoteRepository; + + /** @var Hash */ + private $hashService; + + /** @var Payment */ + private $paymentResource; + /** - * @param SearchCriteriaBuilder $searchCriteriaBuilder - * @param SortOrderBuilder $sortOrderBuilder - * @param OrderRepositoryInterface $orderRepository * @param PaymentActionInterfaceFactory $paymentActionInterfaceFactory * @param SdkProxy $sdkProxy * @param CommandPoolInterface $commandPool + * @param QuoteRepository $quoteRepository + * @param Hash $hashService + * @param Payment $paymentResource * @param LoggerInterface $logger */ public function __construct( - SearchCriteriaBuilder $searchCriteriaBuilder, - SortOrderBuilder $sortOrderBuilder, - OrderRepositoryInterface $orderRepository, PaymentActionInterfaceFactory $paymentActionInterfaceFactory, SdkProxy $sdkProxy, CommandPoolInterface $commandPool, + QuoteRepository $quoteRepository, + Hash $hashService, + Payment $paymentResource, LoggerInterface $logger ) { - $this->sortOrderBuilder = $sortOrderBuilder; - $this->searchCriteriaBuilder = $searchCriteriaBuilder; - $this->orderRepository = $orderRepository; $this->paymentActionInterfaceFactory = $paymentActionInterfaceFactory; $this->sdkProxy = $sdkProxy; $this->commandPool = $commandPool; + $this->quoteRepository = $quoteRepository; + $this->hashService = $hashService; + $this->paymentResource = $paymentResource; $this->logger = $logger; } @@ -92,20 +91,29 @@ public function __construct( */ public function execute(string $cartId, ?string $customerId = null): array { - $order = $this->getOrderByCartIdAndCustomerId($cartId, $customerId); + $quote = $this->quoteRepository->get($cartId); + $quote->reserveOrderId(); + $this->quoteRepository->save($quote); - $this->validate($order, $cartId, $customerId); + // create rvvup order + $this->commandPool->get('initialize')->execute(['quote' => $quote]); - if ($order->getPayment()->getAdditionalInformation('is_rvvup_express_payment')) { - $paymentActions = $this->getExpressOrderPaymentActions($order); - } else { - $paymentActions = $this->createRvvupPayment($order); + $this->hashService->saveQuoteHash($quote); + + $payment = $quote->getPayment(); + if ($payment->getAdditionalInformation(Method::EXPRESS_PAYMENT_KEY)) { + if (!$payment->getAdditionalInformation(Method::CREATE_NEW)) { + return $this->getExpressOrderPaymentActions($payment); + } } + // create rvvup payment + $paymentData = $this->createRvvupPayment($quote); + $paymentActionsDataArray = []; try { - foreach ($paymentActions as $paymentAction) { + foreach ($paymentData as $paymentAction) { if (!is_array($paymentAction)) { continue; } @@ -125,7 +133,6 @@ public function execute(string $cartId, ?string $customerId = null): array 'Error loading Payment Actions for user. Failed return result with message: ' . $t->getMessage(), [ 'quote_id' => $cartId, - 'order_id' => $order->getEntityId(), 'customer_id' => $customerId ] ); @@ -136,7 +143,6 @@ public function execute(string $cartId, ?string $customerId = null): array if (empty($paymentActionsDataArray)) { $this->logger->error('Error loading Payment Actions for user. No payment actions found.', [ 'quote_id' => $cartId, - 'order_id' => $order->getEntityId(), 'customer_id' => $customerId ]); @@ -146,64 +152,15 @@ public function execute(string $cartId, ?string $customerId = null): array return $paymentActionsDataArray; } - /** - * @param string $cartId - * @param string|null $customerId - * @return OrderInterface - * @throws LocalizedException - */ - private function getOrderByCartIdAndCustomerId(string $cartId, ?string $customerId = null): OrderInterface - { - try { - $sortOrder = $this->sortOrderBuilder->setDescendingDirection() - ->setField('created_at') - ->create(); - - $this->searchCriteriaBuilder->setPageSize(1) - ->addSortOrder($sortOrder) - ->addFilter('quote_id', $cartId); - - // If customer ID is provided, pass it. - if ($customerId !== null) { - $this->searchCriteriaBuilder->addFilter('customer_id', $customerId); - } - - $searchCriteria = $this->searchCriteriaBuilder->create(); - - $result = $this->orderRepository->getList($searchCriteria); - } catch (Exception $e) { - $this->logger->error('Error loading Payment Actions for order with message: ' . $e->getMessage(), [ - 'quote_id' => $cartId, - 'customer_id' => $customerId - ]); - - throw new LocalizedException(__('Something went wrong')); - } - - $orders = $result->getItems(); - $order = reset($orders); - - if (!$order) { - $this->logger->error('Error loading Payment Actions. No order found.', [ - 'quote_id' => $cartId, - 'customer_id' => $customerId - ]); - - throw new LocalizedException(__('Something went wrong')); - } - - return $order; - } - /** * Get the order payment's paymentActions from its additional information * - * @param OrderInterface $order + * @param PaymentInterface $payment * @return array */ - private function getExpressOrderPaymentActions(OrderInterface $order): array + private function getExpressOrderPaymentActions(PaymentInterface $payment): array { - $id = $order->getPayment()->getAdditionalInformation('rvvup_order_id'); + $id = $payment->getAdditionalInformation('rvvup_order_id'); $rvvupOrder = $this->sdkProxy->getOrder($id); if (!empty($rvvupOrder)) { @@ -224,9 +181,21 @@ private function getExpressOrderPaymentActions(OrderInterface $order): array return []; } - private function createRvvupPayment($order): array + /** + * @param CartInterface $quote + * @return array + * @throws LocalizedException + * @throws AlreadyExistsException + * @throws NotFoundException + * @throws CommandException + */ + private function createRvvupPayment(CartInterface $quote): array { - $result = $this->commandPool->get('createPayment')->execute(['payment' => $order->getPayment()]); + $payment = $quote->getPayment(); + $result = $this->commandPool->get('createPayment')->execute(['payment' => $payment]); + $id = $result['data']['paymentCreate']['id']; + $payment->setAdditionalInformation(Method::PAYMENT_ID, $id); + $this->paymentResource->save($payment); return $result['data']['paymentCreate']['summary']['paymentActions']; } @@ -234,11 +203,11 @@ private function createRvvupPayment($order): array * Create & return a PaymentActionInterface Data object. * * @param array $paymentAction - * @return \Rvvup\Payments\Api\Data\PaymentActionInterface + * @return PaymentActionInterface */ private function getPaymentActionDataObject(array $paymentAction): PaymentActionInterface { - /** @var \Rvvup\Payments\Api\Data\PaymentActionInterface $paymentActionData */ + /** @var PaymentActionInterface $paymentActionData */ $paymentActionData = $this->paymentActionInterfaceFactory->create(); if (isset($paymentAction['type'])) { @@ -256,40 +225,4 @@ private function getPaymentActionDataObject(array $paymentAction): PaymentAction return $paymentActionData; } - - /** - * @param OrderInterface $order - * @param string $cartId - * @param string|null $customerId - * @return void - * @throws LocalizedException - */ - private function validate(OrderInterface $order, string $cartId, ?string $customerId = null): void - { - $payment = $order->getPayment(); - - // Fail-safe, all orders should have an associated payment record - if ($payment === null) { - $this->logger->error('Error loading Payment Actions for user. No order payment found.', [ - 'quote_id' => $cartId, - 'order_id' => $order->getEntityId(), - 'customer_id' => $customerId, - ]); - - throw new LocalizedException(__('Something went wrong')); - } - - $paymentAdditionalInformation = $payment->getAdditionalInformation(); - - if (!isset($paymentAdditionalInformation['rvvup_order_id'])) { - $this->logger->error('Error loading Payment Actions. No order id additional information found.', [ - 'quote_id' => $cartId, - 'order_id' => $order->getEntityId(), - 'payment_id' => $payment->getEntityId(), - 'customer_id' => $customerId - ]); - - throw new LocalizedException(__('Something went wrong')); - } - } } diff --git a/Model/ProcessOrderResult.php b/Model/ProcessOrderResult.php index 25be364f..c99bca9d 100644 --- a/Model/ProcessOrderResult.php +++ b/Model/ProcessOrderResult.php @@ -5,10 +5,26 @@ namespace Rvvup\Payments\Model; use Magento\Framework\DataObject; +use Magento\Framework\Message\ManagerInterface; use Rvvup\Payments\Api\Data\ProcessOrderResultInterface; class ProcessOrderResult extends DataObject implements ProcessOrderResultInterface { + /** @var ManagerInterface */ + private $messageManager; + + /** + * @param ManagerInterface $messageManager + * @param array $data + */ + public function __construct( + ManagerInterface $messageManager, + array $data = [] + ) { + $this->messageManager = $messageManager; + parent::__construct($data); + } + /** * Get the result type. * @@ -78,6 +94,25 @@ public function setCustomerMessage(string $customerMessage): void $this->setData(self::CUSTOMER_MESSAGE, $customerMessage); } + public function setSessionMessage(?string $messageGroup = null): void + { + // If no message to display, no action. + if ($this->getCustomerMessage() === null) { + return; + } + + switch ($this->getResultType()) { + case ProcessOrderResultInterface::RESULT_TYPE_SUCCESS: + $this->messageManager->addSuccessMessage(__($this->getCustomerMessage()), $messageGroup); + break; + case ProcessOrderResultInterface::RESULT_TYPE_ERROR: + $this->messageManager->addErrorMessage(__($this->getCustomerMessage()), $messageGroup); + break; + default: + $this->messageManager->addWarningMessage(__($this->getCustomerMessage()), $messageGroup); + } + } + /** * Get the default result types. * diff --git a/Model/ProcessRefund/Complete.php b/Model/ProcessRefund/Complete.php index 658c6094..fa8c7346 100644 --- a/Model/ProcessRefund/Complete.php +++ b/Model/ProcessRefund/Complete.php @@ -11,7 +11,7 @@ use Magento\Sales\Api\OrderItemRepositoryInterface; use Psr\Log\LoggerInterface; use Rvvup\Payments\Exception\PaymentValidationException; -use Rvvup\Payments\Service\Order; +use Rvvup\Payments\Service\Capture; class Complete implements ProcessorInterface { @@ -33,26 +33,26 @@ class Complete implements ProcessorInterface private $orderItemRepository; /** - * @var Order + * @var Capture */ - private $orderService; + private $captureService; /** * @param LoggerInterface $logger * @param Json $serializer * @param OrderItemRepositoryInterface $orderItemRepository - * @param Order $orderService + * @param Capture $captureService */ public function __construct( LoggerInterface $logger, Json $serializer, OrderItemRepositoryInterface $orderItemRepository, - Order $orderService + Capture $captureService ) { $this->logger = $logger; $this->serializer = $serializer; $this->orderItemRepository = $orderItemRepository; - $this->orderService = $orderService; + $this->captureService = $captureService; } /** @@ -63,7 +63,7 @@ public function __construct( */ public function execute(array $payload): void { - $order = $this->orderService->getOrderByRvvupId($payload['order_id']); + $order = $this->captureService->getOrderByRvvupId($payload['order_id']); if (!$order->getId()) { $this->writeErrorMessage($payload); diff --git a/Model/Queue/Handler/Handler.php b/Model/Queue/Handler/Handler.php index 70f3d313..8cef7a0f 100644 --- a/Model/Queue/Handler/Handler.php +++ b/Model/Queue/Handler/Handler.php @@ -10,7 +10,7 @@ use Rvvup\Payments\Model\ConfigInterface; use Rvvup\Payments\Model\Payment\PaymentDataGetInterface; use Rvvup\Payments\Model\ProcessOrder\ProcessorPool; -use Rvvup\Payments\Service\Order; +use Rvvup\Payments\Service\Capture; class Handler { @@ -29,9 +29,9 @@ class Handler private $logger; /** - * @var Order + * @var Capture */ - private $orderService; + private $captureService; /** * @param WebhookRepositoryInterface $webhookRepository @@ -40,7 +40,7 @@ class Handler * @param PaymentDataGetInterface $paymentDataGet * @param ProcessorPool $processorPool * @param LoggerInterface $logger - * @param Order $orderService + * @param Capture $captureService */ public function __construct( WebhookRepositoryInterface $webhookRepository, @@ -49,14 +49,14 @@ public function __construct( PaymentDataGetInterface $paymentDataGet, ProcessorPool $processorPool, LoggerInterface $logger, - Order $orderService + Capture $captureService ) { $this->webhookRepository = $webhookRepository; $this->serializer = $serializer; $this->config = $config; $this->paymentDataGet = $paymentDataGet; $this->processorPool = $processorPool; - $this->orderService = $orderService; + $this->captureService = $captureService; $this->logger = $logger; } @@ -72,7 +72,7 @@ public function execute(int $id) $rvvupOrderId = $payload['order_id']; - $order = $this->orderService->getOrderByRvvupId($rvvupOrderId); + $order = $this->captureService->getOrderByRvvupId($rvvupOrderId); // if Payment method is not Rvvup, exit. if (strpos($order->getPayment()->getMethod(), Method::PAYMENT_TITLE_PREFIX) !== 0) { diff --git a/Model/SdkProxy.php b/Model/SdkProxy.php index 8de18b0e..d54fd027 100644 --- a/Model/SdkProxy.php +++ b/Model/SdkProxy.php @@ -5,6 +5,7 @@ use GuzzleHttp\Client; use Psr\Log\LoggerInterface; use Rvvup\Payments\Model\Environment\GetEnvironmentVersionsInterface; +use Rvvup\Sdk\Exceptions\NetworkException; use Rvvup\Sdk\GraphQlSdkFactory; use Rvvup\Sdk\GraphQlSdk; @@ -163,6 +164,18 @@ public function voidPayment($orderId, $paymentId) return $this->getSubject()->voidPayment($orderId, $paymentId); } + /** + * @param string $orderId + * @param string $paymentId + * @return false|mixed + * @throws \JsonException + * @throws NetworkException + */ + public function paymentCapture(string $orderId, string $paymentId) + { + return $this->getSubject()->paymentCapture($orderId, $paymentId); + } + /** * {@inheritdoc} */ diff --git a/Observer/CartRestrictionsWarning.php b/Observer/CartRestrictionsWarning.php index 63c67b2c..106c8919 100644 --- a/Observer/CartRestrictionsWarning.php +++ b/Observer/CartRestrictionsWarning.php @@ -1,8 +1,10 @@ -config = $config; $this->checkoutSession = $checkoutSession; $this->messages = $messages; $this->messageManager = $messageManager; + $this->request = $request; $this->logger = $logger; } @@ -55,6 +67,14 @@ public function execute(Observer $observer): void if (!$this->config->isActive()) { return; } + + //Disable messaging after being redirected not to override messages. + if ($referer = $this->request->getServer('HTTP_REFERER')) { + if (str_contains($referer, 'rvvup')) { + return; + } + } + $hasRestrictedItems = false; $quote = $this->checkoutSession->getQuote(); foreach ($quote->getAllItems() as $item) { @@ -64,9 +84,11 @@ public function execute(Observer $observer): void } } if ($hasRestrictedItems) { - $this->messageManager->addWarningMessage( - $this->messages->getCheckoutMessage() - ); + if (!$this->messageManager->getMessages()->getItems()) { + $this->messageManager->addWarningMessage( + $this->messages->getCheckoutMessage() + ); + } } } catch (\Exception $e) { // Fail gracefully diff --git a/Observer/DataAssignObserver.php b/Observer/DataAssignObserver.php index e1e8df05..a75cbdf4 100644 --- a/Observer/DataAssignObserver.php +++ b/Observer/DataAssignObserver.php @@ -16,7 +16,8 @@ class DataAssignObserver extends AbstractDataAssignObserver Method::ORDER_ID, Method::DASHBOARD_URL, Method::EXPRESS_PAYMENT_KEY, - Method::EXPRESS_PAYMENT_DATA_KEY + Method::EXPRESS_PAYMENT_DATA_KEY, + Method::TRANSACTION_ID, ]; /** diff --git a/Plugin/LoadPaymentMethods.php b/Plugin/LoadPaymentMethods.php index ff480802..683750f3 100644 --- a/Plugin/LoadPaymentMethods.php +++ b/Plugin/LoadPaymentMethods.php @@ -151,6 +151,9 @@ private function getCountryUsed(CartInterface $quote) if ($address && $address->getShippingMethod()) { if ($address->getShippingRateByCode($address->getShippingMethod())) { $addressId = $address->getShippingRateByCode($address->getShippingMethod())->getAddressId(); + if (!$addressId) { + return $address->getCountryId() ?: false; + } return $quote->getAddressById($addressId)->getCountryId(); } } diff --git a/Service/Capture.php b/Service/Capture.php new file mode 100644 index 00000000..ab6c1ea6 --- /dev/null +++ b/Service/Capture.php @@ -0,0 +1,503 @@ +logger = $logger; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->orderPaymentRepository = $orderPaymentRepository; + $this->orderRepository = $orderRepository; + $this->quoteResource = $quoteResource; + $this->quoteManagement = $quoteManagement; + $this->sdkProxy = $sdkProxy; + $this->paymentDataGet = $paymentDataGet; + $this->processorPool = $processorPool; + $this->collectionFactory = $collectionFactory; + $this->cartRepository = $cartRepository; + $this->resultFactory = $resultFactory; + $this->checkoutSession = $checkoutSession; + $this->checkoutHelper = $checkoutHelper; + $this->order = $order; + $this->orderIncrementChecker = $orderIncrementIdChecker; + $this->hashService = $hashService; + } + + /** + * @param string $rvvupOrderId + * @return OrderInterface + * @throws \Exception + */ + public function getOrderByRvvupId(string $rvvupOrderId): OrderInterface + { + // Saerch for the payment record by the Rvvup order ID which is stored in the credit card field. + $searchCriteria = $this->searchCriteriaBuilder->addFilter( + 'additional_information', + '%' . $rvvupOrderId . '%', + 'like' + )->create(); + + $resultSet = $this->orderPaymentRepository->getList($searchCriteria); + + // We always expect 1 payment object for a Rvvup Order ID. + if ($resultSet->getTotalCount() !== 1) { + $this->logger->warning('Webhook error. Payment not found for order.', [ + 'rvvup_order_id' => $rvvupOrderId, + 'payments_count' => $resultSet->getTotalCount() + ]); + throw new PaymentValidationException(__('Error finding order with rvvup_id ' . $rvvupOrderId)); + } + + $payments = $resultSet->getItems(); + /** @var \Magento\Sales\Api\Data\OrderPaymentInterface $payment */ + $payment = reset($payments); + return $this->orderRepository->get($payment->getParentId()); + } + + /** + * @param string $rvvupId + * @param Quote $quote + * @param string $lastTransactionId + * @return array + */ + public function validate(string $rvvupId, Quote &$quote, string &$lastTransactionId): array + { + // First validate we have a Rvvup Order ID, silently return to basket page. + // A standard Rvvup return should always include `rvvup-order-id` param. + if ($rvvupId === null) { + $this->logger->error('No Rvvup Order ID provided'); + return [ + 'is_valid' => false, + 'redirect_to_cart' => true, + 'restore_quote' => true, + 'message' => '', + 'already_exists' => false + ]; + } + + if (!$quote->getIsActive()) { + return [ + 'is_valid' => false, + 'redirect_to_cart' => false, + 'restore_quote' => false, + 'message' => '', + 'already_exists' => true + ]; + } + + if (!$quote->getItems()) { + $quote = $this->getQuoteByRvvupId($rvvupId); + $lastTransactionId = (string)$quote->getPayment()->getAdditionalInformation('transaction_id'); + } + if (empty($quote->getId())) { + $this->logger->error('Missing quote for Rvvup payment', [$rvvupId, $lastTransactionId]); + $message = __( + 'An error occurred while processing your payment (ID %1). Please contact us. ', + $rvvupId + ); + return [ + 'is_valid' => false, + 'redirect_to_cart' => true, + 'restore_quote' => false, + 'message' => $message, + 'already_exists' => false + ]; + } + + $hash = $quote->getPayment()->getAdditionalInformation('quote_hash'); + $quote->collectTotals(); + $savedHash = $this->hashService->getHashForData($quote); + if ($hash !== $savedHash) { + $this->logger->error( + 'Payment hash is invalid during Rvvup Checkout', + [ + 'payment_id' => $quote->getPayment()->getEntityId(), + 'quote_id' => $quote->getId(), + 'last_transaction_id' => $lastTransactionId, + 'rvvup_order_id' => $rvvupId + ] + ); + + $message = __( + 'Your cart was modified after making payment request, please place order again. ' . $rvvupId + ); + return [ + 'is_valid' => false, + 'redirect_to_cart' => true, + 'restore_quote' => false, + 'message' => $message, + 'already_exists' => false + ]; + } + if ($rvvupId !== $lastTransactionId) { + $this->logger->error( + 'Payment transaction id is invalid during Rvvup Checkout', + [ + 'payment_id' => $quote->getPayment()->getEntityId(), + 'quote_id' => $quote->getId(), + 'last_transaction_id' => $lastTransactionId, + 'rvvup_order_id' => $rvvupId + ] + ); + $message = __( + 'This checkout cannot complete, a new cart was opened in another tab. ' . $rvvupId + ); + return [ + 'is_valid' => false, + 'redirect_to_cart' => true, + 'restore_quote' => false, + 'message' => $message, + 'already_exists' => false + ]; + } + + if ($quote->getReservedOrderId()) { + if ($this->orderIncrementChecker->isIncrementIdUsed($quote->getReservedOrderId())) { + return [ + 'is_valid' => false, + 'redirect_to_cart' => false, + 'restore_quote' => false, + 'message' => '', + 'already_exists' => true + ]; + } + } + + return [ + 'is_valid' => true, + 'redirect_to_cart' => false, + 'restore_quote' => false, + 'message' => '', + 'already_exists' => false + ]; + } + + /** + * @param string $rvvupId + * @param Quote $quote + * @return array + */ + public function createOrder(string $rvvupId, Quote $quote): array + { + $this->quoteResource->beginTransaction(); + $lastTransactionId = (string)$quote->getPayment()->getAdditionalInformation('transaction_id'); + $payment = $quote->getPayment(); + + try { + $orderId = $this->quoteManagement->placeOrder($quote->getEntityId(), $payment); + $this->quoteResource->commit(); + return ['id' => $orderId, 'reserved' => false]; + } catch (NoSuchEntityException $e) { + return ['id' => $quote->getReservedOrderId(), 'reserved' => true]; + } catch (\Exception $e) { + $this->quoteResource->rollback(); + if (str_contains($e->getMessage(), AdapterInterface::ERROR_ROLLBACK_INCOMPLETE_MESSAGE)) { + return ['id' => $quote->getReservedOrderId(), 'reserved' => true]; + } + $this->logger->error( + 'Order placement within rvvup payment failed', + [ + 'payment_id' => $payment->getEntityId(), + 'last_transaction_id' => $lastTransactionId, + 'rvvup_order_id' => $rvvupId, + 'message' => $e->getMessage() + ] + ); + return ['id' => false, 'reserved' => false]; + } + } + + /** + * @param Payment $payment + * @param string $lastTransactionId + * @param string $rvvupPaymentId + * @param string $rvvupId + * @return bool + */ + public function paymentCapture( + Quote\Payment $payment, + string $lastTransactionId, + string $rvvupPaymentId, + string $rvvupId + ): bool { + try { + if ($payment->getMethodInstance()->getCaptureType() !== 'MANUAL') { + $this->sdkProxy->paymentCapture($lastTransactionId, $rvvupPaymentId); + } + } catch (\Exception $e) { + $this->logger->error( + 'Order placement failed during payment capture', + [ + 'payment_id' => $payment->getEntityId(), + 'last_transaction_id' => $lastTransactionId, + 'rvvup_order_id' => $rvvupId, + 'message' => $e->getMessage() + ] + ); + return false; + } + return true; + } + + /** + * Update Magento Order based on Rvuup Order and payment statuses + * @param string|null $orderId + * @param string $rvvupId + * @param bool $reservedOrderId + * @return Redirect + */ + public function processOrderResult(?string $orderId, string $rvvupId, bool $reservedOrderId = false): Redirect + { + if (!$orderId) { + return $this->resultFactory->create(ResultFactory::TYPE_REDIRECT)->setPath( + In::SUCCESS, + ['_secure' => true] + ); + } + + try { + if ($reservedOrderId) { + $order = $this->order->loadByIncrementId($orderId); + } else { + $order = $this->orderRepository->get($orderId); + } + // Then get the Rvvup Order by its ID. Rvvup's Redirect In action should always have the correct ID. + $rvvupData = $this->paymentDataGet->execute($rvvupId); + + if ($rvvupData['status'] != $rvvupData['payments'][0]['status']) { + if ($rvvupData['payments'][0]['status'] !== Method::STATUS_AUTHORIZED) { + $this->processorPool->getProcessor($rvvupData['status'])->execute($order, $rvvupData); + } + } + + $processor = $this->processorPool->getProcessor($rvvupData['payments'][0]['status']); + $result = $processor->execute($order, $rvvupData); + if (get_class($processor) == Cancel::class) { + return $this->processResultPage($result, true); + } + return $this->processResultPage($result, false); + } catch (\Exception $e) { + $this->logger->error('Error while processing Rvvup Order status with message: ' . $e->getMessage(), [ + 'rvvup_order_id' => $rvvupId, + 'rvvup_order_status' => $rvvupData['payments'][0]['status'] ?? '' + ]); + + if (isset($order)) { + $order->addStatusToHistory( + $order->getStatus(), + 'Failed to update Magento order from Rvvup order status check', + false + ); + $this->orderRepository->save($order); + } + + return $this->resultFactory->create(ResultFactory::TYPE_REDIRECT)->setPath( + In::SUCCESS, + ['_secure' => true] + ); + } + } + + /** + * @param string $rvvupId + * @return Quote|null + */ + public function getQuoteByRvvupId(string $rvvupId): ?Quote + { + /** @var \Magento\Quote\Model\ResourceModel\Quote\Payment\Collection $collection */ + $collection = $this->collectionFactory->create(); + $collection->addFieldToFilter( + 'additional_information', + [ + 'like' => "%\"rvvup_order_id\":\"$rvvupId\"%" + ] + ); + $items = $collection->getItems(); + if (count($items) !== 1) { + return null; + } + $quoteId = end($items)->getQuoteId(); + try { + return $this->cartRepository->get($quoteId); + } catch (NoSuchEntityException $ex) { + return null; + } + } + + /** + * Set checkout method + * + * @param Quote $quote + * @return void + */ + public function setCheckoutMethod(Quote &$quote): void + { + if ($quote->getCustomer() && $quote->getCustomer()->getId()) { + $quote->setCheckoutMethod(Onepage::METHOD_CUSTOMER); + return; + } + if (!$quote->getCheckoutMethod()) { + if ($this->checkoutHelper->isAllowedGuestCheckout($quote)) { + $quote->setCheckoutMethod(Onepage::METHOD_GUEST); + } else { + $quote->setCheckoutMethod(Onepage::METHOD_REGISTER); + } + } + } + + /** + * @param ProcessOrderResultInterface $result + * @param bool $restoreQuote + * @return Redirect + */ + private function processResultPage(ProcessOrderResultInterface $result, bool $restoreQuote): Redirect + { + if ($restoreQuote) { + $this->checkoutSession->restoreQuote(); + } + + /** @var Redirect $redirect */ + $redirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); + + $params = ['_secure' => true]; + + // If specifically we are redirecting the user to the checkout page, + // set the redirect to the payment step + // and set the messages to be added to the custom group. + if ($result->getRedirectPath() === IN::FAILURE) { + $params['_fragment'] = 'payment'; + $messageGroup = SessionMessageInterface::MESSAGE_GROUP; + } + + $result->setSessionMessage($messageGroup ?? null); + + $redirect->setPath($result->getRedirectPath(), $params); + + return $redirect; + } +} diff --git a/Service/Hash.php b/Service/Hash.php new file mode 100644 index 00000000..b56cdb59 --- /dev/null +++ b/Service/Hash.php @@ -0,0 +1,67 @@ +paymentResource = $paymentResource; + } + + /** + * Save current quote state to hash + * @param Quote $quote + * @return void + * @throws LocalizedException + */ + public function saveQuoteHash(Quote $quote): void + { + $payment = $quote->getPayment(); + $payment->setAdditionalInformation('quote_hash', $this->getHashForData($quote)); + $this->paymentResource->save($payment); + } + + /** + * Create hash for current quote state + * @param Quote $quote + * @return string + */ + public function getHashForData(Quote $quote): string + { + $hashedValues = []; + foreach ($quote->getTotals() as $total) { + $hashedValues[$total->getCode()] = $total->getValue(); + } + $items = $quote->getItems() ?: $quote->getItemsCollection()->getItems(); + foreach ($items as $item) { + $hashedValues[$item->getSku()] = $item->getQty() . '_' . $item->getPrice(); + } + + $output = implode( + ', ', + array_map( + function ($v, $k) { + return sprintf("%s='%s'", $k, $v); + }, + $hashedValues, + array_keys($hashedValues) + ) + ); + + return hash('sha256', $output); + } +} diff --git a/Service/Order.php b/Service/Order.php deleted file mode 100644 index 6442e73c..00000000 --- a/Service/Order.php +++ /dev/null @@ -1,100 +0,0 @@ -logger = $logger; - $this->searchCriteriaBuilder = $searchCriteriaBuilder; - $this->orderPaymentRepository = $orderPaymentRepository; - $this->orderRepository = $orderRepository; - } - - /** - * @param string $rvvupOrderId - * @return OrderInterface - */ - public function getOrderByRvvupId(string $rvvupOrderId): OrderInterface - { - // Saerch for the payment record by the Rvvup order ID which is stored in the credit card field. - $searchCriteria = $this->searchCriteriaBuilder->addFilter( - OrderPaymentInterface::CC_TRANS_ID, - $rvvupOrderId - )->create(); - - $resultSet = $this->orderPaymentRepository->getList($searchCriteria); - - // We always expect 1 payment object for a Rvvup Order ID. - if ($resultSet->getTotalCount() !== 1) { - $this->logger->warning('Webhook error. Payment not found for order.', [ - 'rvvup_order_id' => $rvvupOrderId, - 'payments_count' => $resultSet->getTotalCount() - ]); - } - - $payments = $resultSet->getItems(); - /** @var \Magento\Sales\Api\Data\OrderPaymentInterface $payment */ - $payment = reset($payments); - return $this->orderRepository->get($payment->getParentId()); - } - - /** - * @param CartInterface $quote - * @return array - */ - public function getAllOrdersByQuote(CartInterface $quote): array - { - if (!empty($quote->getEntityId())) { - $searchCriteria = $this->searchCriteriaBuilder - ->addFilter(OrderInterface::QUOTE_ID, $quote->getEntityId())->create(); - try { - return $this->orderRepository->getList($searchCriteria)->getItems(); - } catch (\Exception $e) { - return []; - } - } - return []; - } -} diff --git a/composer.json b/composer.json index ff9ba2ee..a550df4a 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ "guzzlehttp/guzzle": ">=6", "magento/module-catalog": "^103.0 || ^104.0", "magento/module-grouped-product": ">=100.1", - "rvvup/sdk": "0.13.7", + "rvvup/sdk": "0.13.8", "ext-json": "*", "php": "^7.3 || ^8.0" }, diff --git a/etc/di.xml b/etc/di.xml index e4760288..f93a5ce5 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -5,43 +5,43 @@ + type="Rvvup\Payments\Model\PaymentAction"/> + type="Rvvup\Payments\Model\ExpressPaymentCreate"/> + type="Rvvup\Payments\Model\GuestExpressPaymentCreate"/> + type="Rvvup\Payments\Model\GuestCartExpressPaymentRemove"/> + type="Rvvup\Payments\Model\CartExpressPaymentRemove"/> + type="Rvvup\Payments\Model\CartPaymentActionsGet"/> - + type="Rvvup\Payments\Model\GuestCartPaymentActionsGet"/> + + type="Rvvup\Payments\Model\PaymentMethodsAssetsGet"/> + type="Rvvup\Payments\Model\PaymentMethodsSettingsGet"/> + type="Rvvup\Payments\Model\Environment\GetEnvironmentVersions"/> + type="Rvvup\Payments\Model\IsPaymentMethodAvailable"/> + type="Rvvup\Payments\Model\PaymentActionsGet"/> + type="Rvvup\Payments\Model\PaymentMethodsAvailableGet"/> + type="Rvvup\Payments\Model\ProcessOrderResult"/> + type="Rvvup\Payments\Model\Payment\PaymentCreateExpress"/> + type="Rvvup\Payments\Model\Payment\PaymentDataGet"/> + type="Rvvup\Payments\Model\Checks\HasCartExpressPayment"/> @@ -53,7 +53,7 @@ Rvvup - + RvvupLogHandler @@ -67,7 +67,7 @@ Magento\Payment\Block\Form\Cc Rvvup\Payments\Block\Info RvvupValueHandlerPool - RvvupCommandPool + \Rvvup\Payments\Gateway\CommandPool @@ -107,27 +107,38 @@ Rvvup\Payments\Gateway\Command\InitializeCommand Rvvup\Payments\Gateway\Command\Capture - Rvvup\Payments\Gateway\Command\Refund - Rvvup\Payments\Gateway\Command\VoidPayment Rvvup\Payments\Gateway\Command\CreatePayment + + + + Rvvup\Payments\Gateway\Command\Refund + Rvvup\Payments\Gateway\Command\VoidPayment + + + + - + RvvupInitializeRequest Rvvup\Payments\Gateway\Http\TransferFactory Rvvup\Payments\Gateway\Http\Client\TransactionInitialize + xsi:type="object">Rvvup\Payments\Gateway\Http\Client\TransactionInitialize + \Rvvup\Payments\Gateway\Response\InitializeResponseHandler + xsi:type="object">\Rvvup\Payments\Gateway\Response\InitializeResponseHandler + Rvvup\Payments\Gateway\Validator\InitializeResponseValidator + xsi:type="object">Rvvup\Payments\Gateway\Validator\InitializeResponseValidator + RvvupLog - + @@ -172,13 +183,11 @@ - - - - + + type="Rvvup\Payments\Plugin\Quote\Api\PaymentMethodManagement\LimitCartExpressPayment"/> @@ -191,8 +200,9 @@ - + + Magento\Checkout\Model\Session\Proxy RvvupLog @@ -244,7 +254,8 @@ rvvup_payment_methods_restricted_product_specification + xsi:type="string">rvvup_payment_methods_restricted_product_specification + @@ -254,7 +265,8 @@ Rvvup\Payments\Model\Checks\HasCartRestrictedProduct + xsi:type="object">Rvvup\Payments\Model\Checks\HasCartRestrictedProduct + diff --git a/etc/frontend/di.xml b/etc/frontend/di.xml index a905c33c..0eb05b67 100644 --- a/etc/frontend/di.xml +++ b/etc/frontend/di.xml @@ -41,6 +41,13 @@ RvvupLog + + + + Magento\Checkout\Model\Session\Proxy + + + Magento\Checkout\Model\Session\Proxy @@ -99,4 +106,6 @@ Magento\Checkout\Model\Session\Proxy + + diff --git a/phpstan.neon b/phpstan.neon index 127f3c42..a58a5463 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,6 +1,8 @@ parameters: reportUnmatchedIgnoredErrors: false level: 0 + scanDirectories: + - . paths: - . excludePaths: diff --git a/view/frontend/layout/checkout_onepage_success.xml b/view/frontend/layout/checkout_onepage_success.xml new file mode 100644 index 00000000..e46a7a2a --- /dev/null +++ b/view/frontend/layout/checkout_onepage_success.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/view/frontend/requirejs-config.js b/view/frontend/requirejs-config.js index f28374ee..40c07db3 100755 --- a/view/frontend/requirejs-config.js +++ b/view/frontend/requirejs-config.js @@ -15,6 +15,9 @@ var config = { }, 'Magento_Checkout/js/view/billing-address': { 'Rvvup_Payments/js/view/billing-address-mixin': true + }, + 'Magento_Checkout/js/action/place-order': { + 'Rvvup_Payments/js/action/place-order-mixin': true } } }, diff --git a/view/frontend/templates/cart/reload.phtml b/view/frontend/templates/cart/reload.phtml new file mode 100644 index 00000000..7bcf9373 --- /dev/null +++ b/view/frontend/templates/cart/reload.phtml @@ -0,0 +1,8 @@ + diff --git a/view/frontend/web/js/action/checkout/payment/get-order-payment-actions.js b/view/frontend/web/js/action/checkout/payment/get-order-payment-actions.js index d8c70824..436ccac6 100644 --- a/view/frontend/web/js/action/checkout/payment/get-order-payment-actions.js +++ b/view/frontend/web/js/action/checkout/payment/get-order-payment-actions.js @@ -8,6 +8,7 @@ define([ 'Magento_Checkout/js/model/url-builder', 'Rvvup_Payments/js/model/checkout/payment/order-payment-action', 'Rvvup_Payments/js/model/checkout/payment/rvvup-method-properties', + 'Magento_Checkout/js/model/full-screen-loader', ], function ( $, _, @@ -17,7 +18,8 @@ define([ quote, urlBuilder, orderPaymentAction, - rvvupMethodProperties + rvvupMethodProperties, + loader ) { 'use strict'; @@ -96,6 +98,7 @@ define([ throw 'Error placing order'; }).fail(function (response) { + loader.stopLoader(); errorProcessor.process(response, messageContainer); }); }; diff --git a/view/frontend/web/js/action/place-order-mixin.js b/view/frontend/web/js/action/place-order-mixin.js index b9d653c0..4b178688 100644 --- a/view/frontend/web/js/action/place-order-mixin.js +++ b/view/frontend/web/js/action/place-order-mixin.js @@ -8,7 +8,7 @@ define([ return wrapper.wrap(placeOrderAction, function (originalAction, paymentData, redirectOnSuccess) { if (paymentData && paymentData.method.startsWith('rvvup_')) { // do not create order for rvvup payments as it will be created later on. - //return; + return; } return originalAction(paymentData, redirectOnSuccess); diff --git a/view/frontend/web/js/model/checkout/clear-express.js b/view/frontend/web/js/model/checkout/clear-express.js new file mode 100644 index 00000000..64d2513d --- /dev/null +++ b/view/frontend/web/js/model/checkout/clear-express.js @@ -0,0 +1,7 @@ +define(['Magento_Customer/js/customer-data'], function (customerData) { + 'use strict'; + + return function () { + customerData.set('rvvup-express-payment', {}); + }; +}); diff --git a/view/frontend/web/js/view/payment/method-renderer/rvvup-method.js b/view/frontend/web/js/view/payment/method-renderer/rvvup-method.js index 3dad4fbe..3a1ee002 100644 --- a/view/frontend/web/js/view/payment/method-renderer/rvvup-method.js +++ b/view/frontend/web/js/view/payment/method-renderer/rvvup-method.js @@ -58,14 +58,9 @@ define([ quote.paymentMethod.subscribe(function (data) { // If we move away from Paypal method and we already have an order ID then trigger cancel. - if (data.method !== 'rvvup_PAYPAL' && rvvupMethodProperties.getPlacedOrderId() !== null) { + if (isExpressPayment() && data.method !== 'rvvup_PAYPAL') { this.cancelPayPalPayment(); } - - // Make sure Data Method is paypal before we setup the event listener. - if (data.method === 'rvvup_PAYPAL') { - document.addEventListener('click', this.checkDomElement.bind(this)); - } }.bind(this)); window.addEventListener("message", (event) => { @@ -86,10 +81,11 @@ define([ chosenWidth = width > windowWidth ? windowWidth : width, chosenHeight = height > windowHeight ? windowHeight : height, finalWidth = width === "max" ? windowWidth - 40 : chosenWidth, - finalHeight = height === "max" ? windowHeight - 40 : chosenHeight; + /** Remove 80pixels as margin from top */ + finalHeight = height === "max" ? windowHeight - 80 - 40 : chosenHeight; let items = []; - items.push(document.querySelector('.modal-inner-wrap.rvvup')); items.push(document.getElementById(this.getIframeId())); + items.push(document.querySelector('.modal-inner-wrap.rvvup')); items.forEach(function (item) { if (item) { item.animate([{ @@ -111,28 +107,6 @@ define([ } }, false); - /** - * Add event listener on AJAX success event of order placement which returns the order ID. - * Set placedOrderId attribute to use if required from the component. We expect an integer. - * Set the value only if the payment was done via a Rvvup payment component. - */ - $(document).ajaxSuccess(function (event, xhr, settings) { - if (settings.type !== 'POST' || - xhr.status !== 200 || - !settings.url.includes('/payment-information') || - !xhr.hasOwnProperty('responseJSON') - ) { - return; - } - - /* Check we are in current component, by our model is defined */ - if (typeof rvvupMethodProperties === 'undefined') { - return; - } - - /* if response is a positive integer, set it as the order ID. */ - rvvupMethodProperties.setPlacedOrderId(/^\d+$/.test(xhr.responseJSON) ? xhr.responseJSON : null); - }); /* Cancel Express Payment on click event. */ $(document).on('click', 'a#' + this.getCancelExpressPaymentLinkId(), (e) => { e.preventDefault(); @@ -149,24 +123,13 @@ define([ }) }, - checkDomElement: function(event) { - // Setup elements we want to make sure we cancel on. - const elements = document.querySelectorAll('button.action, span[id="block-discount-heading"], span[id="block-giftcard-heading"], .opc-progress-bar-item, input[id="billing-address-same-as-shipping-rvvup_PAYPAL"]'); - // Only check if we have a placeOrderID this shows if we have clicked on the cards - if (rvvupMethodProperties.getPlacedOrderId() !== null) { - // If we are not in the boundary and have clicked on the elements above cancel payment. - if(Array.from(elements).some(element => element.contains(event.target))) { - this.cancelPayPalPayment(); - document.removeEventListener("click", this.checkDomElement); - } - } - }, - cancelPayPalPayment: function () { var url = orderPaymentAction.getCancelUrl(); this.resetDefaultData(); loader.stopLoader(); - this.showModal(url); + if (url) { + this.showModal(url); + } }, getPaypalBlockStyling: function () { @@ -202,7 +165,6 @@ define([ formId: "st-form", submitCallback: function (data) { var submitData = { - order_id: rvvupMethodProperties.getPlacedOrderId(), auth: data.jwt, form_key: $.mage.cookies.get('form_key') }; @@ -221,7 +183,7 @@ define([ $.ajax({ type: "POST", data: data, - url: url.build('rvvup/cardpayments/cancel'), + url: url.build('rvvup/payment/cancel'), complete: function (e) { $('body').trigger("processStop"); }, @@ -296,7 +258,7 @@ define([ $.ajax({ type: "POST", data: data, - url: url.build('rvvup/cardpayments/cancel'), + url: url.build('rvvup/payment/cancel'), complete: function (e) { $('body').trigger("processStop"); }, @@ -360,11 +322,7 @@ define([ self.isPlaceOrderActionAllowed(false); return self.getPlaceOrderDeferredObject() .done(function () { - if (rvvupMethodProperties.getPlacedOrderId() !== null) { - return actions.resolve(); - } - - return actions.reject(); + return actions.resolve(); }).fail(function () { return actions.reject(); }).always(function () { @@ -554,15 +512,13 @@ define([ * Handle setting cancel URL in the modal, prevents multiple clicks. */ triggerModalCancelUrl: function () { - if (!orderPaymentAction.getCancelUrl() - || rvvupMethodProperties.getIsCancellationTriggered() === true - || this.preventClose) { - return; - } - - rvvupMethodProperties.setIsCancellationTriggered(true); - - this.setIframeUrl(orderPaymentAction.getCancelUrl()); + let data = {form_key: $.mage.cookies.get('form_key')}; + $.ajax({ + type: "POST", + data: data, + url: url.build('rvvup/payment/cancel'), + }); + window.location.reload(); }, /** @@ -653,6 +609,7 @@ define([ if (code === 'rvvup_CARD' && rvvup_parameters.settings.card.flow === "INLINE") { window.SecureTrading.updateJWT(orderPaymentAction.getPaymentToken()); $("#tp_place_order").trigger("click"); + return; } if (orderPaymentAction.getRedirectUrl() !== null) {