Skip to content
Code-Schnipsel Gruppen Projekte
Payment.php 25,9 KiB
Newer Older
Patrick Müller's avatar
Patrick Müller committed
<?php

/**
Patrick Müller's avatar
Patrick Müller committed
 * This file contains QUI\ERP\Payments\Amazon\Payment
Patrick Müller's avatar
Patrick Müller committed
 */

namespace QUI\ERP\Payments\Amazon;

Patrick Müller's avatar
Patrick Müller committed
use AmazonPay\Client as AmazonPayClient;
Patrick Müller's avatar
Patrick Müller committed
use AmazonPay\ResponseInterface;
Patrick Müller's avatar
Patrick Müller committed
use QUI;
use QUI\ERP\Order\AbstractOrder;
Patrick Müller's avatar
Patrick Müller committed
use QUI\ERP\Order\Handler as OrderHandler;
use QUI\ERP\Accounting\Payments\Gateway\Gateway;
Patrick Müller's avatar
Patrick Müller committed

/**
 * Class Payment
 */
class Payment extends QUI\ERP\Accounting\Payments\Api\AbstractPayment
{
Patrick Müller's avatar
Patrick Müller committed
    /**
     * Amazon API Order attributes
     */
    const ATTR_AUTHORIZATION_REFERENCE_IDS = 'amazon-AuthorizationReferenceIds';
    const ATTR_AMAZON_AUTHORIZATION_ID     = 'amazon-AmazonAuthorizationId';
    const ATTR_AMAZON_CAPTURE_ID           = 'amazon-AmazonCaptureId';
    const ATTR_AMAZON_ORDER_REFERENCE_ID   = 'amazon-OrderReferenceId';
    const ATTR_CAPTURE_REFERENCE_IDS       = 'amazon-CaptureReferenceIds';
Patrick Müller's avatar
Patrick Müller committed
    const ATTR_ORDER_AUTHORIZED            = 'amazon-OrderAuthorized';
    const ATTR_ORDER_CAPTURED              = 'amazon-OrderCaptures';
Patrick Müller's avatar
Patrick Müller committed
    const ATTR_ORDER_REFERENCE_SET         = 'amazon-OrderReferenceSet';
    const ATTR_RECONFIRM_ORDER             = 'amazon-ReconfirmOrder';
Patrick Müller's avatar
Patrick Müller committed

    /**
     * Setting options
     */
    const SETTING_ARTICLE_TYPE_MIXED    = 'mixed';
    const SETTING_ARTICLE_TYPE_PHYSICAL = 'physical';
    const SETTING_ARTICLE_TYPE_DIGITAL  = 'digital';

Patrick Müller's avatar
Patrick Müller committed
    /**
     * Amazon Pay PHP SDK Client
     *
     * @var AmazonPayClient
     */
    protected $AmazonPayClient = null;

Patrick Müller's avatar
Patrick Müller committed
    /**
     * Current Order that is being processed
     *
     * @var AbstractOrder
     */
    protected $Order = null;

Patrick Müller's avatar
Patrick Müller committed
    /**
     * @return string
     */
    public function getTitle()
    {
Patrick Müller's avatar
Patrick Müller committed
        return $this->getLocale()->get('quiqqer/payment-amazon', 'payment.title');
Patrick Müller's avatar
Patrick Müller committed
    }

    /**
     * @return string
     */
    public function getDescription()
    {
Patrick Müller's avatar
Patrick Müller committed
        return $this->getLocale()->get('quiqqer/payment-amazon', 'payment.description');
Patrick Müller's avatar
Patrick Müller committed
    }

Patrick Müller's avatar
Patrick Müller committed
    /**
     * Return the payment icon (the URL path)
     * Can be overwritten
     *
     * @return string
     */
    public function getIcon()
    {
        return URL_OPT_DIR.'quiqqer/payment-amazon/bin/images/Payment.jpg';
    }

Patrick Müller's avatar
Patrick Müller committed
    /**
Patrick Müller's avatar
Patrick Müller committed
     * Is the payment process successful?
Patrick Müller's avatar
Patrick Müller committed
     * This method returns the payment success type
     *
     * @param string $hash - Vorgangsnummer - hash number - procedure number
     * @return bool
     */
    public function isSuccessful($hash)
    {
Patrick Müller's avatar
Patrick Müller committed
        try {
            $Order = OrderHandler::getInstance()->getOrderByHash($hash);
        } catch (\Exception $Exception) {
            QUI\System\Log::addError(
                'Amazon Pay :: Cannot check if payment process for Order #'.$hash.' is successful'
                .' -> '.$Exception->getMessage()
Patrick Müller's avatar
Patrick Müller committed
            );

            return false;
        }

        return $Order->getPaymentDataEntry(self::ATTR_ORDER_AUTHORIZED);
Patrick Müller's avatar
Patrick Müller committed
    }
Patrick Müller's avatar
Patrick Müller committed

    /**
     * Is the payment a gateway payment?
     *
     * @return bool
     */
    public function isGateway()
    {
        return true;
    }

    /**
     * @return bool
     */
    public function refundSupport()
    {
        return true;
    }

    /**
     * Execute the request from the payment provider
     *
     * @param QUI\ERP\Accounting\Payments\Gateway\Gateway $Gateway
Patrick Müller's avatar
Patrick Müller committed
     * @return void
     *
Patrick Müller's avatar
Patrick Müller committed
     * @throws QUI\ERP\Accounting\Payments\Transactions\Exception
Patrick Müller's avatar
Patrick Müller committed
     * @throws QUI\Exception
Patrick Müller's avatar
Patrick Müller committed
     */
    public function executeGatewayPayment(QUI\ERP\Accounting\Payments\Gateway\Gateway $Gateway)
    {
        $AmazonPay = $this->getAmazonPayClient();
        $Order     = $Gateway->getOrder();
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
        $Order->addHistory('Amazon Pay :: Check if payment from Amazon was successful');
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
        $amazonCaptureId = $Order->getPaymentDataEntry(self::ATTR_AMAZON_CAPTURE_ID);
Patrick Müller's avatar
Patrick Müller committed

        $Response = $AmazonPay->getCaptureDetails([
Patrick Müller's avatar
Patrick Müller committed
            'amazon_capture_id' => $amazonCaptureId
Patrick Müller's avatar
Patrick Müller committed

        try {
            $response = $this->getResponseData($Response);
        } catch (AmazonPayException $Exception) {
            $Order->addHistory(
                'Amazon Pay :: An error occurred while trying to validate the Capture -> '.$Exception->getMessage()
            );

            $Order->update(QUI::getUsers()->getSystemUser());
            return;
        }
Patrick Müller's avatar
Patrick Müller committed

        // check the amount that has already been captured
        $PriceCalculation   = $Order->getPriceCalculation();
        $targetSum          = $PriceCalculation->getSum()->precision(2)->get();
Patrick Müller's avatar
Patrick Müller committed
        $targetCurrencyCode = $Order->getCurrency()->getCode();

        $captureData        = $response['GetCaptureDetailsResult']['CaptureDetails'];
        $actualSum          = $captureData['CaptureAmount']['Amount'];
        $actualCurrencyCode = $captureData['CaptureAmount']['CurrencyCode'];
Patrick Müller's avatar
Patrick Müller committed

        if ($actualSum < $targetSum) {
            $Order->addHistory(
                'Amazon Pay :: The amount that was captured from Amazon was less than the'
                .' total sum of the order. Total sum: '.$targetSum.' '.$targetCurrencyCode
                .' | Actual sum captured by Amazon: '.$actualSum.' '.$actualCurrencyCode
Patrick Müller's avatar
Patrick Müller committed
            );

            $Order->update(QUI::getUsers()->getSystemUser());
Patrick Müller's avatar
Patrick Müller committed
            return;
        }

        // book payment in QUIQQER ERP
        $Order->addHistory('Amazo Pay :: Finalize Order payment');

Patrick Müller's avatar
Patrick Müller committed
        $Gateway->purchase(
            $actualSum,
Patrick Müller's avatar
Patrick Müller committed
            new QUI\ERP\Currency\Currency($actualCurrencyCode),
Patrick Müller's avatar
Patrick Müller committed
            $Order,
            $this
        );
Patrick Müller's avatar
Patrick Müller committed

        $Order->addHistory('Amazo Pay :: Closing OrderReference');
        $this->closeOrderReference($Order);
        $Order->addHistory('Amazo Pay :: Order successfully paid');

        $Order->update(QUI::getUsers()->getSystemUser());
Patrick Müller's avatar
Patrick Müller committed
    }

    /**
     * Execute a refund
     *
     * @param QUI\ERP\Accounting\Payments\Transactions\Transaction $Transaction
     */
    public function refund(QUI\ERP\Accounting\Payments\Transactions\Transaction $Transaction)
    {
        // @todo
    }

    /**
     * If the Payment method is a payment gateway, it can return a gateway display
     *
     * @param AbstractOrder $Order
     * @param QUI\ERP\Order\Controls\OrderProcess\Processing $Step
     * @return string
Patrick Müller's avatar
Patrick Müller committed
     *
     * @throws QUI\Exception
Patrick Müller's avatar
Patrick Müller committed
     */
    public function getGatewayDisplay(AbstractOrder $Order, $Step = null)
    {
        $Control = new PaymentDisplay();
        $Control->setAttribute('Order', $Order);
        $Control->setAttribute('Payment', $this);

        $Step->setTitle(
            QUI::getLocale()->get(
                'quiqqer/payment-amazon',
                'payment.step.title'
            )
        );

Patrick Müller's avatar
Patrick Müller committed
        $Engine = QUI::getTemplateManager()->getEngine();
        $Step->setContent($Engine->fetch(dirname(__FILE__).'/PaymentDisplay.Header.html'));
Patrick Müller's avatar
Patrick Müller committed

        return $Control->create();
    }

    /**
Patrick Müller's avatar
Patrick Müller committed
     * Authorize the payment for an Order with Amazon
Patrick Müller's avatar
Patrick Müller committed
     *
     * @param string $orderReferenceId
     * @param AbstractOrder $Order
     *
     * @throws AmazonPayException
     * @throws QUI\ERP\Exception
     * @throws QUI\Exception
Patrick Müller's avatar
Patrick Müller committed
     */
Patrick Müller's avatar
Patrick Müller committed
    public function authorizePayment($orderReferenceId, AbstractOrder $Order)
Patrick Müller's avatar
Patrick Müller committed
    {
Patrick Müller's avatar
Patrick Müller committed
        $Order->addHistory('Amazon Pay :: Authorize payment');

        if ($Order->getPaymentDataEntry(self::ATTR_ORDER_AUTHORIZED)) {
            $Order->addHistory('Amazon Pay :: Authorization already exist');
            return;
        }

        $AmazonPay        = $this->getAmazonPayClient();
        $PriceCalculation = $Order->getPriceCalculation();
        $reconfirmOrder   = $Order->getPaymentDataEntry(self::ATTR_RECONFIRM_ORDER);
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
        // Re-confirm Order after previously declined Authorization because of "InvalidPaymentMethod"
        if ($reconfirmOrder) {
            $Order->addHistory(
                'Amazon Pay :: Re-confirm Order after declined Authorization because of "InvalidPaymentMethod"'
            );
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
            $orderReferenceId = $Order->getPaymentDataEntry(self::ATTR_AMAZON_ORDER_REFERENCE_ID);
Patrick Müller's avatar
Patrick Müller committed

            $Response = $AmazonPay->confirmOrderReference([
Patrick Müller's avatar
Patrick Müller committed
                'amazon_order_reference_id' => $orderReferenceId
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
            $this->getResponseData($Response); // check response data

            $Order->setPaymentData(self::ATTR_RECONFIRM_ORDER, false);

            $Order->addHistory('Amazon Pay :: OrderReference re-confirmed');
        } elseif (!$Order->getPaymentDataEntry(self::ATTR_ORDER_REFERENCE_SET)) {
            $Order->addHistory(
                'Amazon Pay :: Setting details of the Order to Amazon Pay API'
            );

            $Response = $AmazonPay->setOrderReferenceDetails([
Patrick Müller's avatar
Patrick Müller committed
                'amazon_order_reference_id' => $orderReferenceId,
                'amount'                    => $PriceCalculation->getSum()->precision(2)->get(),
Patrick Müller's avatar
Patrick Müller committed
                'currency_code'             => $Order->getCurrency()->getCode(),
                'seller_order_id'           => $Order->getPrefixedId(),
                'seller_note'               => $this->getSellerNote($Order),
                'custom_information'        => QUI::getLocale()->get(
                    'quiqqer/payment-amazon',
                    'Payment.order_custom_information',
                    [
                        'orderHash' => $Order->getHash()
                    ]
                )
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
            $response              = $this->getResponseData($Response);
            $orderReferenceDetails = $response['SetOrderReferenceDetailsResult']['OrderReferenceDetails'];
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
            if (isset($orderReferenceDetails['Constraints']['Constraint']['ConstraintID'])) {
                $Order->addHistory(
                    'Amazon Pay :: An error occurred while setting the details of the Order: "'
                    .$orderReferenceDetails['Constraints']['Constraint']['ConstraintID'].'""'
Patrick Müller's avatar
Patrick Müller committed
                );

                $this->throwAmazonPayException(
                    $orderReferenceDetails['Constraints']['Constraint']['ConstraintID'],
Patrick Müller's avatar
Patrick Müller committed
                        'reRenderWallet' => 1
Patrick Müller's avatar
Patrick Müller committed
                );
            }

            $AmazonPay->confirmOrderReference([
Patrick Müller's avatar
Patrick Müller committed
                'amazon_order_reference_id' => $orderReferenceId
Patrick Müller's avatar
Patrick Müller committed

            $Order->setPaymentData(self::ATTR_ORDER_REFERENCE_SET, true);
            $Order->update(QUI::getUsers()->getSystemUser());
Patrick Müller's avatar
Patrick Müller committed
        }

Patrick Müller's avatar
Patrick Müller committed
        $Order->addHistory('Amazon Pay :: Requesting new Authorization');
Patrick Müller's avatar
Patrick Müller committed

        $authorizationReferenceId = $this->getNewAuthorizationReferenceId($Order);
Patrick Müller's avatar
Patrick Müller committed

        $Response = $AmazonPay->authorize([
Patrick Müller's avatar
Patrick Müller committed
            'amazon_order_reference_id'  => $orderReferenceId,
            'authorization_amount'       => $PriceCalculation->getSum()->precision(2)->get(),
Patrick Müller's avatar
Patrick Müller committed
            'currency_code'              => $Order->getCurrency()->getCode(),
Patrick Müller's avatar
Patrick Müller committed
            'authorization_reference_id' => $authorizationReferenceId,
            'transaction_timeout'        => 0  // get authorization status synchronously
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
        $response = $this->getResponseData($Response);
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
        // save reference ids in $Order
        $authorizationDetails  = $response['AuthorizeResult']['AuthorizationDetails'];
        $amazonAuthorizationId = $authorizationDetails['AmazonAuthorizationId'];

        $this->addAuthorizationReferenceIdToOrder($authorizationReferenceId, $Order);
Patrick Müller's avatar
Patrick Müller committed
        $Order->setPaymentData(self::ATTR_AMAZON_AUTHORIZATION_ID, $amazonAuthorizationId);
        $Order->setPaymentData(self::ATTR_AMAZON_ORDER_REFERENCE_ID, $orderReferenceId);
        $Order->addHistory(
            QUI::getLocale()->get(
                'quiqqer/payment-amazon',
                'history.order_reference_id',
                [
                    'orderReferenceId' => $orderReferenceId
                ]
            )
        );
Patrick Müller's avatar
Patrick Müller committed

        $Order->update(QUI::getUsers()->getSystemUser());

Patrick Müller's avatar
Patrick Müller committed
        // check Authorization
        $Order->addHistory('Amazon Pay :: Checking Authorization status');

        $status = $authorizationDetails['AuthorizationStatus'];
        $state  = $status['State'];

        switch ($state) {
            case 'Open':
                // everything is fine
                $Order->addHistory(
                    'Amazon Pay :: Authorization is OPEN an can be used for capturing'
                );

                $Order->setPaymentData(self::ATTR_ORDER_AUTHORIZED, true);
                $Order->update(QUI::getUsers()->getSystemUser());
                break;

            case 'Declined':
                $reason = $status['ReasonCode'];

                switch ($reason) {
                    case 'InvalidPaymentMethod':
                        $Order->addHistory(
                            'Amazon Pay :: Authorization was DECLINED. User has to choose another payment method.'
Patrick Müller's avatar
Patrick Müller committed
                        );

                        $Order->setPaymentData(self::ATTR_RECONFIRM_ORDER, true);
Patrick Müller's avatar
Patrick Müller committed
                        $Order->update(QUI::getUsers()->getSystemUser());

                        $this->throwAmazonPayException($reason, [
Patrick Müller's avatar
Patrick Müller committed
                            'reRenderWallet' => 1
Patrick Müller's avatar
Patrick Müller committed
                        break;

                    case 'TransactionTimedOut':
                        $Order->addHistory(
                            'Amazon Pay :: Authorization was DECLINED. User has to choose another payment method.'
Patrick Müller's avatar
Patrick Müller committed
                        );

                        $AmazonPay->cancelOrderReference([
Patrick Müller's avatar
Patrick Müller committed
                            'amazon_order_reference_id' => $orderReferenceId,
                            'cancelation_reason'        => 'Order #'.$Order->getHash().' could not be authorized :: TransactionTimedOut'
                        ]);
Patrick Müller's avatar
Patrick Müller committed

                        $Order->setPaymentData(self::ATTR_ORDER_REFERENCE_SET, false);
                        $Order->update(QUI::getUsers()->getSystemUser());

                        $this->throwAmazonPayException($reason, [
Patrick Müller's avatar
Patrick Müller committed
                            'reRenderWallet' => 1,
                            'orderCancelled' => 1
Patrick Müller's avatar
Patrick Müller committed
                        break;

                    default:
                        $Order->addHistory(
                            'Amazon Pay :: Authorization was DECLINED. OrderReference has to be closed. Cannot use Amazon Pay for this Order.'
Patrick Müller's avatar
Patrick Müller committed
                        );

                        $Response = $AmazonPay->getOrderReferenceDetails([
Patrick Müller's avatar
Patrick Müller committed
                            'amazon_order_reference_id' => $orderReferenceId
Patrick Müller's avatar
Patrick Müller committed

                        $response              = $Response->toArray();
                        $orderReferenceDetails = $response['GetOrderReferenceDetailsResult']['OrderReferenceDetails'];
                        $orderReferenceStatus  = $orderReferenceDetails['OrderReferenceStatus']['State'];

                        if ($orderReferenceStatus === 'Open') {
                            $AmazonPay->cancelOrderReference([
Patrick Müller's avatar
Patrick Müller committed
                                'amazon_order_reference_id' => $orderReferenceId,
                                'cancelation_reason'        => 'Order #'.$Order->getHash().' could not be authorized'
                            ]);
Patrick Müller's avatar
Patrick Müller committed

                            $Order->setPaymentData(self::ATTR_AMAZON_ORDER_REFERENCE_ID, false);
                        }

                        $Order->clearPayment();
                        $Order->update(QUI::getUsers()->getSystemUser());

                        $this->throwAmazonPayException($reason);
Patrick Müller's avatar
Patrick Müller committed
                }
                break;

            default:
                // @todo Order ggf. pending
Patrick Müller's avatar
Patrick Müller committed
                $reason = $status['ReasonCode'];

                $Order->addHistory(
                    'Amazon Pay :: Authorization cannot be used because it is in state "'.$state.'".'
                    .' ReasonCode: "'.$reason.'"'
Patrick Müller's avatar
Patrick Müller committed
                );
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
                $this->throwAmazonPayException($reason);
        }
Patrick Müller's avatar
Patrick Müller committed
    }

    /**
Patrick Müller's avatar
Patrick Müller committed
     * Capture the actual payment for an Order
     *
     * @param AbstractOrder $Order
     * @return void
Patrick Müller's avatar
Patrick Müller committed
     * @throws AmazonPayException
     * @throws QUI\ERP\Exception
     * @throws QUI\Exception
Patrick Müller's avatar
Patrick Müller committed
     */
    public function capturePayment(AbstractOrder $Order)
    {
Patrick Müller's avatar
Patrick Müller committed
        $Order->addHistory('Amazon Pay :: Capture payment');

        if ($Order->getPaymentDataEntry(self::ATTR_ORDER_CAPTURED)) {
            $Order->addHistory('Amazon Pay :: Capture is already completed');
            return;
        }
Patrick Müller's avatar
Patrick Müller committed

        $AmazonPay        = $this->getAmazonPayClient();
Patrick Müller's avatar
Patrick Müller committed
        $orderReferenceId = $Order->getPaymentDataEntry(self::ATTR_AMAZON_ORDER_REFERENCE_ID);

        if (empty($orderReferenceId)) {
Patrick Müller's avatar
Patrick Müller committed
            $Order->addHistory(
                'Amazon Pay :: Capture failed because the Order has no AmazonOrderReferenceId'
            );

Patrick Müller's avatar
Patrick Müller committed
                'quiqqer/payment-amazon',
                'exception.Payment.capture.not_authorized',
Patrick Müller's avatar
Patrick Müller committed
                    'orderHash' => $Order->getHash()
Patrick Müller's avatar
Patrick Müller committed
        }

        try {
            $this->authorizePayment($orderReferenceId, $Order);
        } catch (AmazonPayException $Exception) {
Patrick Müller's avatar
Patrick Müller committed
            $Order->addHistory(
Patrick Müller's avatar
Patrick Müller committed
                'Amazon Pay :: Capture failed because the Order has no OPEN Authorization'
Patrick Müller's avatar
Patrick Müller committed
            );

Patrick Müller's avatar
Patrick Müller committed
                'quiqqer/payment-amazon',
                'exception.Payment.capture.not_authorized',
Patrick Müller's avatar
Patrick Müller committed
                    'orderHash' => $Order->getHash()
Patrick Müller's avatar
Patrick Müller committed
        } catch (\Exception $Exception) {
Patrick Müller's avatar
Patrick Müller committed
            $Order->addHistory(
                'Amazon Pay :: Capture failed because of an error: '.$Exception->getMessage()
Patrick Müller's avatar
Patrick Müller committed
            );

Patrick Müller's avatar
Patrick Müller committed
            QUI\System\Log::writeException($Exception);
            return;
        }

        $PriceCalculation   = $Order->getPriceCalculation();
        $sum                = $PriceCalculation->getSum()->precision(2)->get();
Patrick Müller's avatar
Patrick Müller committed
        $captureReferenceId = $this->getNewCaptureReferenceId($Order);

        $Response = $AmazonPay->capture([
Patrick Müller's avatar
Patrick Müller committed
            'amazon_authorization_id' => $Order->getPaymentDataEntry(self::ATTR_AMAZON_AUTHORIZATION_ID),
Patrick Müller's avatar
Patrick Müller committed
            'capture_amount'          => $sum,
            'currency_code'           => $Order->getCurrency()->getCode(),
            'capture_reference_id'    => $captureReferenceId,
            'seller_capture_note'     => $this->getSellerNote($Order)
Patrick Müller's avatar
Patrick Müller committed

        $response = $this->getResponseData($Response);

Patrick Müller's avatar
Patrick Müller committed
        $captureDetails  = $response['CaptureResult']['CaptureDetails'];
        $amazonCaptureId = $captureDetails['AmazonCaptureId'];

        $this->addCaptureReferenceIdToOrder($amazonCaptureId, $Order);
Patrick Müller's avatar
Patrick Müller committed
        $Order->setPaymentData(self::ATTR_AMAZON_CAPTURE_ID, $amazonCaptureId);
        $Order->update(QUI::getUsers()->getSystemUser());

        // Check Capture
        $Order->addHistory('Amazon Pay :: Checking Capture status');
Patrick Müller's avatar
Patrick Müller committed

        $status = $captureDetails['CaptureStatus'];
        $state  = $status['State'];

        switch ($state) {
            case 'Completed':
                $Order->addHistory(
                    'Amazon Pay :: Capture is COMPLETED -> '.$sum.' '.$Order->getCurrency()->getCode()
                );

                $Order->setPaymentData(self::ATTR_ORDER_CAPTURED, true);
                $Order->update(QUI::getUsers()->getSystemUser());

                $Gateway = Gateway::getInstance();
                $Gateway->setOrder($Order);

                $this->executeGatewayPayment($Gateway);
            case 'Pending':
                // @todo pending status
                if (Provider::isIpnHandlingActivated()) {
                    // etc.
                }
                break;
            default:
                $reason = $status['ReasonCode'];

                $Order->addHistory(
                    'Amazon Pay :: Capture operation failed with state "'.$state.'".'
                    .' ReasonCode: "'.$reason.'"'
                );

                // @todo Change order status to "problems with Amazon Payment"

                $this->throwAmazonPayException($reason);
                break;
        }
Patrick Müller's avatar
Patrick Müller committed
    }

    /**
     * Set the Amazon Pay OrderReference to status CLOSED
     *
Patrick Müller's avatar
Patrick Müller committed
     * @param AbstractOrder $Order
     * @param string $reason (optional) - Close reason [default: "Order #hash completed"]
Patrick Müller's avatar
Patrick Müller committed
     * @return void
     */
Patrick Müller's avatar
Patrick Müller committed
    protected function closeOrderReference(AbstractOrder $Order, $reason = null)
Patrick Müller's avatar
Patrick Müller committed
    {
        $AmazonPay        = $this->getAmazonPayClient();
Patrick Müller's avatar
Patrick Müller committed
        $orderReferenceId = $Order->getPaymentDataEntry(self::ATTR_AMAZON_ORDER_REFERENCE_ID);
Patrick Müller's avatar
Patrick Müller committed

        $AmazonPay->closeOrderReference([
Patrick Müller's avatar
Patrick Müller committed
            'amazon_order_reference_id' => $orderReferenceId,
            'closure_reason'            => $reason ?: 'Order #'.$Order->getHash().' completed'
        ]);
Patrick Müller's avatar
Patrick Müller committed
    }

    /**
     * Check if the Amazon Pay API response is OK
     *
     * @param ResponseInterface $Response - Amazon Pay API Response
     * @return array
     * @throws AmazonPayException
     */
    protected function getResponseData(ResponseInterface $Response)
    {
        $response = $Response->toArray();

        if (!empty($response['Error']['Code'])) {
            $this->throwAmazonPayException($response['Error']['Code']);
        }

        return $response;
    }

    /**
     * Throw AmazonPayException for specific Amazon API Error
Patrick Müller's avatar
Patrick Müller committed
     *
     * @param string $errorCode
Patrick Müller's avatar
Patrick Müller committed
     * @param array $exceptionAttributes (optional) - Additional Exception attributes that may be relevant for the Frontend
Patrick Müller's avatar
Patrick Müller committed
     * @return string
     *
     * @throws AmazonPayException
     */
    protected function throwAmazonPayException($errorCode, $exceptionAttributes = [])
Patrick Müller's avatar
Patrick Müller committed
    {
Patrick Müller's avatar
Patrick Müller committed
        $L   = $this->getLocale();
        $lg  = 'quiqqer/payment-amazon';
        $msg = $L->get($lg, 'payment.error_msg.general_error');
Patrick Müller's avatar
Patrick Müller committed

        switch ($errorCode) {
            case 'InvalidPaymentMethod':
Patrick Müller's avatar
Patrick Müller committed
            case 'PaymentMethodNotAllowed':
Patrick Müller's avatar
Patrick Müller committed
            case 'TransactionTimedOut':
            case 'AmazonRejected':
Patrick Müller's avatar
Patrick Müller committed
            case 'ProcessingFailure':
Patrick Müller's avatar
Patrick Müller committed
            case 'MaxCapturesProcessed':
                $msg = $L->get($lg, 'payment.error_msg.'.$errorCode);
Patrick Müller's avatar
Patrick Müller committed
                break;
        }

        $Exception = new AmazonPayException($msg);
Patrick Müller's avatar
Patrick Müller committed
        $Exception->setAttributes($exceptionAttributes);
Patrick Müller's avatar
Patrick Müller committed

        throw $Exception;
    }

    /**
     * Generate a unique, random Authorization Reference ID to identify
     * authorization transactions for an order
     *
     * @param AbstractOrder $Order
     * @return string
     */
Patrick Müller's avatar
Patrick Müller committed
    protected function getNewAuthorizationReferenceId(AbstractOrder $Order)
    {
        return mb_substr('a_'.$Order->getId().'_'.uniqid(), 0, 32);
Patrick Müller's avatar
Patrick Müller committed
    }

    /**
Patrick Müller's avatar
Patrick Müller committed
     * Add an AuthorizationReferenceId to current Order
Patrick Müller's avatar
Patrick Müller committed
     *
     * @param string $authorizationReferenceId
     * @param AbstractOrder $Order
Patrick Müller's avatar
Patrick Müller committed
     * @return void
     */
    protected function addAuthorizationReferenceIdToOrder($authorizationReferenceId, AbstractOrder $Order)
Patrick Müller's avatar
Patrick Müller committed
    {
        $authorizationReferenceIds = $Order->getPaymentDataEntry(self::ATTR_AUTHORIZATION_REFERENCE_IDS);
Patrick Müller's avatar
Patrick Müller committed

        if (empty($authorizationReferenceIds)) {
Patrick Müller's avatar
Patrick Müller committed
        }

        $authorizationReferenceIds[] = $authorizationReferenceId;

        $Order->setPaymentData(self::ATTR_AUTHORIZATION_REFERENCE_IDS, $authorizationReferenceIds);
        $Order->update(QUI::getUsers()->getSystemUser());
Patrick Müller's avatar
Patrick Müller committed
    }

    /**
     * Generate a unique, random CaptureReferenceId to identify
     * captures for an order
     *
     * @param AbstractOrder $Order
     * @return string
     */
    protected function getNewCaptureReferenceId(AbstractOrder $Order)
Patrick Müller's avatar
Patrick Müller committed
    {
        return mb_substr('c_'.$Order->getId().'_'.uniqid(), 0, 32);
Patrick Müller's avatar
Patrick Müller committed
    }

    /**
Patrick Müller's avatar
Patrick Müller committed
     * Add an CaptureReferenceId to current Order
Patrick Müller's avatar
Patrick Müller committed
     *
     * @param string $captureReferenceId
     * @param AbstractOrder $Order
Patrick Müller's avatar
Patrick Müller committed
     * @return void
     */
    protected function addCaptureReferenceIdToOrder($captureReferenceId, AbstractOrder $Order)
Patrick Müller's avatar
Patrick Müller committed
    {
        $captureReferenceIds = $Order->getPaymentDataEntry(self::ATTR_CAPTURE_REFERENCE_IDS);
Patrick Müller's avatar
Patrick Müller committed

        if (empty($captureReferenceIds)) {
Patrick Müller's avatar
Patrick Müller committed
        }

        $captureReferenceIds[] = $captureReferenceId;

        $Order->setPaymentData(self::ATTR_CAPTURE_REFERENCE_IDS, $captureReferenceIds);
        $Order->update(QUI::getUsers()->getSystemUser());
Patrick Müller's avatar
Patrick Müller committed
    }

    /**
     * Get order seller note
     *
     * The seller note is a custom message that is shown to the customer
     * at their Amazon account for an oder
     *
     * @param AbstractOrder $Order
     * @return string
     * @throws QUI\Exception
     */
    protected function getSellerNote(AbstractOrder $Order)
    {
        $Conf        = QUI::getPackage('quiqqer/payment-amazon')->getConfig();
        $description = $Conf->get('payment', 'amazon_seller_note');

        if (empty($description)) {
            $description = [];
        } else {
            $description = json_decode($description, true);
        }

        $lang            = $Order->getCustomer()->getLang();
        $descriptionText = '';

        if (!empty($description[$lang])) {
            $descriptionText = str_replace(['{orderId}'], [$this->Order->getPrefixedId()], $description[$lang]);
        // max length 255
        $descriptionText = mb_substr($descriptionText, 0 , 255);

Patrick Müller's avatar
Patrick Müller committed
    /**
     * Get Amazon Pay Client for current payment process
     *
     * @return AmazonPayClient
     */
    protected function getAmazonPayClient()
    {
        if (!is_null($this->AmazonPayClient)) {
            return $this->AmazonPayClient;
        }

        $this->AmazonPayClient = new AmazonPayClient([
Patrick Müller's avatar
Patrick Müller committed
            'merchant_id' => Provider::getApiSetting('merchant_id'),
            'access_key'  => Provider::getApiSetting('access_key'),
            'secret_key'  => Provider::getApiSetting('secret_key'),
            'client_id'   => Provider::getApiSetting('client_id'),
            'sandbox'     => boolval(Provider::getApiSetting('sandbox')),
            'region'      => Provider::getApiSetting('region')
Patrick Müller's avatar
Patrick Müller committed

        return $this->AmazonPayClient;
    }
Patrick Müller's avatar
Patrick Müller committed
}