Skip to content
Code-Schnipsel Gruppen Projekte
Payment.php 25,9 KiB
Newer Older
  • Learn to ignore specific revisions
  • 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);
    
    
    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
    
    
    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
    }