Newer
Older
TillQliro / Model / Api / Client / OrderManagement.php
@Jonas Jonsson Jonas Jonsson on 2 Apr 2024 12 KB Initial
<?php
/**
 * Copyright © Qliro AB. All rights reserved.
 * See LICENSE.txt for license details.
 */

namespace Qliro\QliroOne\Model\Api\Client;

use GuzzleHttp\Exception\RequestException;
use Magento\Framework\Serialize\Serializer\Json;
use Qliro\QliroOne\Api\Data\AdminCancelOrderRequestInterface;
use Qliro\QliroOne\Api\Data\AdminMarkItemsAsShippedRequestInterface;
use Qliro\QliroOne\Api\Data\AdminOrderInterface;
use Qliro\QliroOne\Api\Data\AdminOrderPaymentTransactionInterface;
use Qliro\QliroOne\Api\Data\AdminReturnWithItemsRequestInterface;
use Qliro\QliroOne\Api\Data\AdminTransactionResponseInterface;
use Qliro\QliroOne\Api\Data\AdminUpdateMerchantReferenceRequestInterface;
use Qliro\QliroOne\Api\Data\CheckoutStatusInterface;
use Qliro\QliroOne\Api\Data\QliroOrderInterfaceFactory;
use Qliro\QliroOne\Model\Api\Client\Exception\ClientException;
use Qliro\QliroOne\Model\Api\Client\Exception\OrderManagementApiException;
use Qliro\QliroOne\Model\Api\Service;
use Qliro\QliroOne\Model\Config;
use Qliro\QliroOne\Model\ContainerMapper;
use Qliro\QliroOne\Model\Exception\TerminalException;
use Qliro\QliroOne\Model\Logger\Manager as LogManager;

/**
 * Order Management API client class
 */
class OrderManagement implements \Qliro\QliroOne\Api\Client\OrderManagementInterface
{
    /**
     * @var \Qliro\QliroOne\Model\Api\Service
     */
    private $service;

    /**
     * @var \Qliro\QliroOne\Model\Config
     */
    private $config;

    /**
     * @var \Qliro\QliroOne\Model\ContainerMapper
     */
    private $containerMapper;

    /**
     * @var \Magento\Framework\Serialize\Serializer\Json
     */
    private $json;

    /**
     * @var \Qliro\QliroOne\Api\Data\QliroOrderInterfaceFactory
     */
    private $qliroOrderFactory;

    /**
     * @var \Qliro\QliroOne\Model\Logger\Manager
     */
    private $logManager;

    /**
     * Inject dependencies
     *
     * @param \Qliro\QliroOne\Model\Api\Service $service
     * @param \Qliro\QliroOne\Model\Config $config
     * @param \Magento\Framework\Serialize\Serializer\Json $json
     * @param \Qliro\QliroOne\Model\ContainerMapper $containerMapper
     * @param \Qliro\QliroOne\Api\Data\QliroOrderInterfaceFactory $qliroOrderFactory
     * @param \Qliro\QliroOne\Model\Logger\Manager $logManager
     */
    public function __construct(
        Service $service,
        Config $config,
        Json $json,
        ContainerMapper $containerMapper,
        QliroOrderInterfaceFactory $qliroOrderFactory,
        LogManager $logManager
    ) {
        $this->service = $service;
        $this->config = $config;
        $this->containerMapper = $containerMapper;
        $this->json = $json;
        $this->qliroOrderFactory = $qliroOrderFactory;
        $this->logManager = $logManager;
    }

    /**
     * Get admin QliroOne order by its Qliro Order ID
     *
     * @param int $qliroOrderId
     * @return \Qliro\QliroOne\Api\Data\AdminOrderInterface
     * @throws \Qliro\QliroOne\Model\Api\Client\Exception\ClientException
     */
    public function getOrder($qliroOrderId)
    {
        $container = null;

        try {
            $response = $this->service->get('checkout/adminapi/orders/{OrderId}', ['OrderId' => $qliroOrderId]);

            /** @var \Qliro\QliroOne\Api\Data\AdminOrderInterface $container */
            $container = $this->containerMapper->fromArray($response, AdminOrderInterface::class);
        } catch (\Exception $exception) {
            $this->handleExceptions($exception);
        }

        return $container;
    }

    /**
     * Send a "Mark items as shipped" request
     *
     * @param \Qliro\QliroOne\Api\Data\AdminMarkItemsAsShippedRequestInterface $request
     * @param int|null $storeId
     * @return \Qliro\QliroOne\Api\Data\AdminTransactionResponseInterface
     * @throws \Qliro\QliroOne\Model\Api\Client\Exception\ClientException
     */
    public function markItemsAsShipped(AdminMarkItemsAsShippedRequestInterface $request, $storeId = null)
    {
        $container = null;

        try {
            $payload = $this->containerMapper->toArray($request);
            $response = $this->service->post('checkout/adminapi/markitemsasshipped/withitems', $payload, $storeId);

            /** @var \Qliro\QliroOne\Api\Data\AdminTransactionResponseInterface $container */
            $container = $this->containerMapper->fromArray($response, AdminTransactionResponseInterface::class);
        } catch (\Exception $exception) {
            $this->handleExceptions($exception);
        }

        return $container;
    }

    /**
     * Cancel admin QliroOne order
     *
     * @param \Qliro\QliroOne\Api\Data\AdminCancelOrderRequestInterface $request
     * @param int|null $storeId
     * @return \Qliro\QliroOne\Api\Data\AdminTransactionResponseInterface
     * @throws \Qliro\QliroOne\Model\Api\Client\Exception\ClientException
     */
    public function cancelOrder(AdminCancelOrderRequestInterface $request, $storeId = null)
    {
        $container = null;

        try {
            $payload = $this->containerMapper->toArray($request);
            $response = $this->service->post('checkout/adminapi/cancelorder', $payload, $storeId);

            /** @var \Qliro\QliroOne\Api\Data\AdminTransactionResponseInterface $container */
            $container = $this->containerMapper->fromArray($response, AdminTransactionResponseInterface::class);
        } catch (\Exception $exception) {
            // Workaround for having cancelOrder NOT throwing exception in case of success
            if ($exception instanceof RequestException) {
                $data = $this->json->unserialize($exception->getResponse()->getBody());

                $errorCode = $data['ErrorCode'] ?? null;

                if ($errorCode === 'ORDER_HAS_BEEN_CANCELLED') {
                    /** @var \Qliro\QliroOne\Api\Data\AdminTransactionResponseInterface $container */
                    $container = $this->containerMapper->fromArray(
                        ['Status' => CheckoutStatusInterface::STATUS_REFUSED],
                        AdminTransactionResponseInterface::class
                    );

                    return $container;
                }
            }

            // Otherwise, handle exceptions as usual
            $this->handleExceptions($exception);
        }

        return $container;
    }

    /**
     * Update QliroOne order merchant reference
     *
     * @param \Qliro\QliroOne\Api\Data\AdminUpdateMerchantReferenceRequestInterface $request
     * @param int|null $storeId
     * @return \Qliro\QliroOne\Api\Data\AdminTransactionResponseInterface
     * @throws \Qliro\QliroOne\Model\Api\Client\Exception\ClientException
     */
    public function updateMerchantReference(AdminUpdateMerchantReferenceRequestInterface $request, $storeId = null)
    {
        $container = null;

        try {
            $payload = $this->containerMapper->toArray($request);
            $response = $this->service->post('checkout/adminapi/updatemerchantreference', $payload, $storeId);

            /** @var \Qliro\QliroOne\Api\Data\AdminTransactionResponseInterface $container */
            $container = $this->containerMapper->fromArray($response, AdminTransactionResponseInterface::class);
        } catch (\Exception $exception) {
            /**
             * This function is called inside a notification from Qliro. That notification should just respond with ok
             * Unless something is very wrong. What the call to updatemerchantreference responds with should NOT
             * make any difference in what that notification should respond! The return from this function only logs
             * the transactionId....
             * @todo This needs to be fixed properly once we can debug notifications
             */

            // Workaround for having updateMerchantReference NOT throwing exception in case of success
//            if ($exception instanceof RequestException) {
//                $data = $this->json->unserialize($exception->getResponse()->getBody());
//
//                $errorCode = $data['ErrorCode'] ?? null;
//
//                if ($errorCode === 'ORDER_HAS_BEEN_CANCELLED') {
//                    /** @var \Qliro\QliroOne\Api\Data\AdminTransactionResponseInterface $container */
//                    $container = $this->containerMapper->fromArray(
//                        ['Status' => CheckoutStatusInterface::STATUS_REFUSED],
//                        AdminTransactionResponseInterface::class
//                    );
//
//                    return $container;
//                }
//            }
//
//            $this->handleExceptions($exception);
        }

        return $container;
    }

    /**
     * Make a call "Return with items"
     * @todo Not used?
     *
     * @param \Qliro\QliroOne\Api\Data\AdminReturnWithItemsRequestInterface $request
     * @param int|null $storeId
     * @return \Qliro\QliroOne\Api\Data\AdminTransactionResponseInterface
     * @throws \Qliro\QliroOne\Model\Api\Client\Exception\ClientException
     */
    public function returnWithItems(AdminReturnWithItemsRequestInterface $request, $storeId = null)
    {
        $container = null;

        try {
            $payload = $this->containerMapper->toArray($request);
            $response = $this->service->post('checkout/adminapi/returnwithitems', $payload, $storeId);

            /** @var \Qliro\QliroOne\Api\Data\AdminTransactionResponseInterface $container */
            $container = $this->containerMapper->fromArray($response, AdminTransactionResponseInterface::class);
        } catch (\Exception $exception) {
            $this->handleExceptions($exception);
        }

        return $container;
    }

    /**
     * Get admin QliroOne order payment transaction
     * @todo Not used?
     *
     * @param int $paymentTransactionId
     * @param int|null $storeId
     * @return \Qliro\QliroOne\Api\Data\AdminOrderPaymentTransactionInterface
     * @throws \Qliro\QliroOne\Model\Api\Client\Exception\ClientException
     */
    public function getPaymentTransaction($paymentTransactionId, $storeId = null)
    {
        $container = null;

        try {
            $response = $this->service->get(
                'checkout/adminapi/paymenttransactions/{PaymentTransactionId}',
                ['PaymentTransactionId' => $paymentTransactionId],
                $storeId
            );

            /** @var \Qliro\QliroOne\Api\Data\AdminOrderPaymentTransactionInterface $container */
            $container = $this->containerMapper->fromArray($response, AdminOrderPaymentTransactionInterface::class);
        } catch (\Exception $exception) {
            $this->handleExceptions($exception);
        }

        return $container;
    }

    /**
     * Retry a reversal payment
     *
     * @param int $paymentReference
     * @param int|null $storeId
     * @return \Qliro\QliroOne\Api\Data\AdminOrderPaymentTransactionInterface|null
     * @throws ClientException
     */
    public function retryReversalPayment($paymentReference, $storeId = null)
    {
        $container = null;

        try {
            $response = $this->service->post(
                'checkout/adminapi/retryreversalpaymenttransaction',
                ['PaymentReference' => $paymentReference],
                $storeId
            );

            /** @var \Qliro\QliroOne\Api\Data\AdminOrderPaymentTransactionInterface $container */
            $container = $this->containerMapper->fromArray($response, AdminOrderPaymentTransactionInterface::class);
        } catch (\Exception $exception) {
            $this->handleExceptions($exception);
        }

        return $container;
    }

    /**
     * Handle exceptions that come from the API response
     *
     * @param \Exception $exception
     * @throws \Qliro\QliroOne\Model\Api\Client\Exception\ClientException
     */
    private function handleExceptions(\Exception $exception)
    {
        if ($exception instanceof RequestException) {
            $data = $this->json->unserialize($exception->getResponse()->getBody());

            if (isset($data['ErrorCode']) && isset($data['ErrorMessage'])) {
                if (!($exception instanceof TerminalException)) {
                    $this->logManager->critical($exception, ['extra' => $data]);
                }

                throw new OrderManagementApiException(
                    __('Error [%1]: %2', $data['ErrorCode'], $data['ErrorMessage'])
                );
            }
        }

        if (!($exception instanceof TerminalException)) {
            $this->logManager->critical($exception);
        }

        throw new ClientException(__('Request to Qliro One has failed.'), $exception);
    }
}