/**
  * {@inheritDoc}
  */
 protected function doApproveAndDeposit(PaymentInterface $payment, $amount)
 {
     $instruction = $payment->getPaymentInstruction();
     if (PaymentInstructionInterface::STATE_VALID !== $instruction->getState()) {
         throw new InvalidPaymentInstructionException('PaymentInstruction\'s state must be VALID.');
     }
     $paymentState = $payment->getState();
     if (PaymentInterface::STATE_NEW === $paymentState) {
         if ($instruction->hasPendingTransaction()) {
             throw new InvalidPaymentInstructionException('PaymentInstruction can only ever have one pending transaction.');
         }
         if (1 === Number::compare($amount, $payment->getTargetAmount())) {
             throw new \InvalidArgumentException('$amount must not be greater than Payment\'s target amount.');
         }
         $transaction = $this->buildFinancialTransaction();
         $transaction->setTransactionType(FinancialTransactionInterface::TRANSACTION_TYPE_APPROVE_AND_DEPOSIT);
         $transaction->setPayment($payment);
         $transaction->setRequestedAmount($amount);
         $payment->addTransaction($transaction);
         $payment->setApprovingAmount($amount);
         $payment->setDepositingAmount($amount);
         $payment->setState(PaymentInterface::STATE_APPROVING);
         $instruction->setApprovingAmount($instruction->getApprovingAmount() + $amount);
         $instruction->setDepositingAmount($instruction->getDepositingAmount() + $amount);
         $retry = false;
     } else {
         if (PaymentInterface::STATE_APPROVING === $paymentState) {
             if (0 !== Number::compare($amount, $payment->getApprovingAmount())) {
                 throw new \InvalidArgumentException('$amount must be equal to Payment\'s approving amount.');
             }
             if (0 !== Number::compare($amount, $payment->getDepositingAmount())) {
                 throw new \InvalidArgumentException('$amount must be equal to Payment\'s depositing amount.');
             }
             $transaction = $payment->getApproveTransaction();
             $retry = true;
         } else {
             throw new InvalidPaymentException('Payment\'s state must e NEW, or APPROVING.');
         }
     }
     $plugin = $this->getPlugin($instruction->getPaymentSystemName());
     try {
         $plugin->approveAndDeposit($transaction, $retry);
         if (PluginInterface::RESPONSE_CODE_SUCCESS === $transaction->getResponseCode()) {
             $transaction->setState(FinancialTransactionInterface::STATE_SUCCESS);
             $processedAmount = $transaction->getProcessedAmount();
             $payment->setState(PaymentInterface::STATE_APPROVED);
             $payment->setApprovingAmount(0.0);
             $payment->setDepositingAmount(0.0);
             $payment->setApprovedAmount($processedAmount);
             $payment->setDepositedAmount($processedAmount);
             $instruction->setApprovingAmount($instruction->getApprovingAmount() - $amount);
             $instruction->setDepositingAmount($instruction->getDepositingAmount() - $amount);
             $instruction->setApprovedAmount($instruction->getApprovedAmount() + $processedAmount);
             $instruction->setDepositedAmount($instruction->getDepositedAmount() + $processedAmount);
             return $this->buildFinancialTransactionResult($transaction, Result::STATUS_SUCCESS, PluginInterface::REASON_CODE_SUCCESS);
         } else {
             $transaction->setState(FinancialTransactionInterface::STATE_FAILED);
             $payment->setState(PaymentInterface::STATE_FAILED);
             $payment->setApprovingAmount(0.0);
             $payment->setDepositingAmount(0.0);
             $instruction->setApprovingAmount($instruction->getApprovingAmount() - $amount);
             $instruction->setDepositingAmount($instruction->getDepositingAmount() - $amount);
             return $this->buildFinancialTransactionResult($transaction, Result::STATUS_FAILED, $transaction->getReasonCode());
         }
     } catch (PluginFinancialException $ex) {
         $transaction->setState(FinancialTransactionInterface::STATE_FAILED);
         $payment->setState(PaymentInterface::STATE_FAILED);
         $payment->setApprovingAmount(0.0);
         $payment->setDepositingAmount(0.0);
         $instruction->setApprovingAmount($instruction->getApprovingAmount() - $amount);
         $instruction->setDepositingAmount($instruction->getDepositingAmount() - $amount);
         return $this->buildFinancialTransactionResult($transaction, Result::STATUS_FAILED, $transaction->getReasonCode());
     } catch (PluginBlockedException $blocked) {
         $transaction->setState(FinancialTransactionInterface::STATE_PENDING);
         if ($blocked instanceof PluginTimeoutException) {
             $reasonCode = PluginInterface::REASON_CODE_TIMEOUT;
         } else {
             if ($blocked instanceof PluginActionRequiredException) {
                 $reasonCode = PluginInterface::REASON_CODE_ACTION_REQUIRED;
             } else {
                 if (null === ($reasonCode = $transaction->getReasonCode())) {
                     $reasonCode = PluginInterface::REASON_CODE_BLOCKED;
                 }
             }
         }
         $transaction->setReasonCode($reasonCode);
         $transaction->setResponseCode(PluginInterface::RESPONSE_CODE_PENDING);
         $result = $this->buildFinancialTransactionResult($transaction, Result::STATUS_PENDING, $reasonCode);
         $result->setPluginException($blocked);
         $result->setRecoverable();
         return $result;
     }
 }