Skip to content

Commit

Permalink
De-couple capture service (#84)
Browse files Browse the repository at this point in the history
* De-couple capture service
  • Loading branch information
andrii-onufriichuk authored Feb 21, 2024
1 parent 05bb886 commit 2457ba8
Show file tree
Hide file tree
Showing 7 changed files with 526 additions and 300 deletions.
34 changes: 34 additions & 0 deletions Api/Data/ValidationInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);

namespace Rvvup\Payments\Api\Data;

use Magento\Quote\Model\Quote;

interface ValidationInterface
{
/**
* Properties used
*/
public const IS_VALID = "is_valid";
public const ORDER_ID = "order_id";
public const REDIRECT_TO_CART = "redirect_to_cart";
public const REDIRECT_TO_CHECKOUT_PAYMENT = "redirect_to_checkout_payment";
public const RESTORE_QUOTE = "restore_quote";
public const MESSAGE = "message";
public const ALREADY_EXISTS = "already_exists";

/**
* @param Quote $quote
* @param string $lastTransactionId
* @param string|null $rvvupId
* @param string|null $paymentStatus
* @return ValidationInterface
*/
public function validate(
Quote &$quote,
string &$lastTransactionId,
string $rvvupId = null,
string $paymentStatus = null
): ValidationInterface;
}
57 changes: 30 additions & 27 deletions Controller/Redirect/In.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Message\ManagerInterface;
use Magento\Framework\Session\SessionManagerInterface;
use Rvvup\Payments\Api\Data\ValidationInterface;
use Rvvup\Payments\Gateway\Method;
use Rvvup\Payments\Service\Capture;
use Rvvup\Payments\Service\Result;

class In implements HttpGetActionInterface
{
Expand Down Expand Up @@ -42,25 +44,31 @@ class In implements HttpGetActionInterface
/** @var Capture */
private $captureService;

/** @var Result */
private $resultService;

/**
* @param RequestInterface $request
* @param ResultFactory $resultFactory
* @param SessionManagerInterface $checkoutSession
* @param ManagerInterface $messageManager
* @param Capture $captureService
* @param Result $resultService
*/
public function __construct(
RequestInterface $request,
ResultFactory $resultFactory,
SessionManagerInterface $checkoutSession,
ManagerInterface $messageManager,
Capture $captureService
Capture $captureService,
Result $resultService
) {
$this->request = $request;
$this->resultFactory = $resultFactory;
$this->checkoutSession = $checkoutSession;
$this->messageManager = $messageManager;
$this->captureService = $captureService;
$this->resultService = $resultService;
}

/**
Expand All @@ -72,17 +80,6 @@ public function execute()
$rvvupId = $this->request->getParam('rvvup-order-id');
$paymentStatus = $this->request->getParam('payment-status');

if ($paymentStatus == Method::STATUS_CANCELLED ||
$paymentStatus == Method::STATUS_EXPIRED ||
$paymentStatus == Method::STATUS_DECLINED ||
$paymentStatus == Method::STATUS_AUTHORIZATION_EXPIRED ||
$paymentStatus == Method::STATUS_FAILED) {
return $this->resultFactory->create(ResultFactory::TYPE_REDIRECT)->setPath(
self::FAILURE,
['_secure' => true, '_fragment' => 'payment']
);
}

$quote = $this->checkoutSession->getQuote();

if (!$quote->getId()) {
Expand All @@ -93,48 +90,54 @@ public function execute()
$rvvupPaymentId = $payment->getAdditionalInformation(Method::PAYMENT_ID);
$lastTransactionId = (string)$payment->getAdditionalInformation(Method::TRANSACTION_ID);

$validate = $this->captureService->validate($rvvupId, $quote, $lastTransactionId);
$validate = $this->captureService->validate($quote, $lastTransactionId, $rvvupId, $paymentStatus);

if (!$validate['is_valid']) {
if ($validate['restore_quote']) {
if (!$validate->getIsValid()) {
if ($validate->getRestoreQuote()) {
$this->checkoutSession->restoreQuote();
}
if ($validate['message']) {
if ($validate->getMessage()) {
$this->messageManager->addErrorMessage($validate['message']);
}
if ($validate['redirect_to_cart']) {
if ($validate->getRedirectToCart()) {
return $this->redirectToCart();
}
if ($validate['already_exists']) {
if ($validate->getRedirectToCheckoutPayment()) {
return $this->resultFactory->create(ResultFactory::TYPE_REDIRECT)->setPath(
In::FAILURE,
['_secure' => true, '_fragment' => 'payment']
);
}
if ($validate->getAlreadyExists()) {
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->resultService->processOrderResult(null, $rvvupId);
}
return $this->captureService->processOrderResult((string)$quote->getReservedOrderId(), $rvvupId, true);
return $this->resultService->processOrderResult((string)$quote->getReservedOrderId(), $rvvupId, true);
}
}

$this->captureService->setCheckoutMethod($quote);
$order = $this->captureService->createOrder($rvvupId, $quote);
$orderId = $order['id'];
$reserved = $order['reserved'];
$validation = $this->captureService->createOrder($rvvupId, $quote);
$orderId = $validation->getOrderId();
$alreadyExists = $validation->getAlreadyExists();

if ($reserved) {
if ($alreadyExists) {
$this->checkoutSession->setLastSuccessQuoteId($quote->getId());
$this->checkoutSession->setLastQuoteId($quote->getId());
$this->checkoutSession->setLastOrderId($quote->getReservedOrderId());
$this->checkoutSession->setLastRealOrderId($quote->getReservedOrderId());
return $this->captureService->processOrderResult((string)$orderId, $rvvupId, true);
return $this->resultService->processOrderResult((string)$orderId, $rvvupId, true);
}

if (!$orderId) {
$this->messageManager->addErrorMessage(
__(
'An error occurred while creating your order (ID %1). Please contact us.',
$rvvupId
$quote->getReservedOrderId() ?: $rvvupId
)
);
$this->checkoutSession->restoreQuote();
Expand All @@ -151,7 +154,7 @@ public function execute()
return $this->redirectToCart();
}

return $this->captureService->processOrderResult((string)$orderId, $rvvupId);
return $this->resultService->processOrderResult((string)$orderId, $rvvupId);
}

/**
Expand Down
198 changes: 198 additions & 0 deletions Model/Data/Validation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
<?php

namespace Rvvup\Payments\Model\Data;

use Magento\Framework\DataObject;
use Magento\Quote\Model\Quote;
use Magento\Sales\Model\OrderIncrementIdChecker;
use Rvvup\Payments\Api\Data\ValidationInterface;
use Rvvup\Payments\Api\Data\ValidationInterfaceFactory;
use Rvvup\Payments\Gateway\Method;
use Rvvup\Payments\Service\Hash;
use Psr\Log\LoggerInterface;

class Validation extends DataObject implements ValidationInterface
{
/** Set via di.xml
* @var LoggerInterface
*/
private $logger;

/** @var Hash */
private $hashService;

/** @var OrderIncrementIdChecker */
private $orderIncrementChecker;

/**
* @param Hash|null $hashService
* @param OrderIncrementIdChecker|null $orderIncrementIdChecker
* @param LoggerInterface|null $logger
* @param array $data
*/
public function __construct(
Hash $hashService,
OrderIncrementIdChecker $orderIncrementIdChecker,
LoggerInterface $logger,
array $data = []
) {
$this->logger = $logger;
$this->orderIncrementChecker = $orderIncrementIdChecker;
$this->hashService = $hashService;
parent::__construct($data);
}

public function validate(
Quote &$quote,
string &$lastTransactionId,
string $rvvupId = null,
string $paymentStatus = null
): ValidationInterface {
$data = $this->getDefaultData();

// 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');
$data[ValidationInterface::IS_VALID] = false;
$data[ValidationInterface::REDIRECT_TO_CART] = true;
$data[ValidationInterface::RESTORE_QUOTE] = true;
$this->setValidationData($data);
return $this;
}

if (!$this->isPaymentStatusValid($paymentStatus)) {
$data[ValidationInterface::IS_VALID] = false;
$data[ValidationInterface::REDIRECT_TO_CHECKOUT_PAYMENT] = true;
$data[ValidationInterface::RESTORE_QUOTE] = true;
$this->setValidationData($data);
return $this;
}

/** ID which we will show to customer in case of an error */
$errorId = $quote->getReservedOrderId() ?: $rvvupId;

if (!$quote->getIsActive()) {
$data[ValidationInterface::IS_VALID] = false;
$data[ValidationInterface::ALREADY_EXISTS] = true;
$this->setValidationData($data);
return $this;
}

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. ',
$errorId
);
$data[ValidationInterface::IS_VALID] = false;
$data[ValidationInterface::REDIRECT_TO_CART] = true;
$data[ValidationInterface::RESTORE_QUOTE] = true;
$data[ValidationInterface::MESSAGE] = $message;
$this->setValidationData($data);
return $this;
}

$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. ' . $errorId
);

$data[ValidationInterface::IS_VALID] = false;
$data[ValidationInterface::REDIRECT_TO_CART] = true;
$data[ValidationInterface::RESTORE_QUOTE] = true;
$data[ValidationInterface::MESSAGE] = $message;
$this->setValidationData($data);
return $this;
}
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. ' . $errorId
);
$data[ValidationInterface::IS_VALID] = false;
$data[ValidationInterface::REDIRECT_TO_CART] = true;
$data[ValidationInterface::MESSAGE] = $message;
$this->setValidationData($data);
return $this;
}
if ($quote->getReservedOrderId()) {
if ($this->orderIncrementChecker->isIncrementIdUsed($quote->getReservedOrderId())) {
$data[ValidationInterface::IS_VALID] = false;
$data[ValidationInterface::ALREADY_EXISTS] = true;
$this->setValidationData($data);
return $this;
}
}

$this->setValidationData($data);
return $this;
}

/**
* @return array
*/
private function getDefaultData(): array
{
return [
ValidationInterface::IS_VALID => true,
ValidationInterface::REDIRECT_TO_CART => false,
ValidationInterface::RESTORE_QUOTE => false,
ValidationInterface::MESSAGE => '',
ValidationInterface::REDIRECT_TO_CHECKOUT_PAYMENT => false,
ValidationInterface::ALREADY_EXISTS => false
];
}

/**
* @param string|null $paymentStatus
* @return bool
*/
private function isPaymentStatusValid(?string $paymentStatus): bool
{
if ($paymentStatus == Method::STATUS_CANCELLED ||
$paymentStatus == Method::STATUS_EXPIRED ||
$paymentStatus == Method::STATUS_DECLINED ||
$paymentStatus == Method::STATUS_AUTHORIZATION_EXPIRED ||
$paymentStatus == Method::STATUS_FAILED) {
return false;
}
return true;
}

/**
* @param array $data
* @return void
*/
private function setValidationData(array $data): void
{
foreach ($data as $key => $value) {
$this->setData($key, $value);
}
}
}
Loading

0 comments on commit 2457ba8

Please sign in to comment.