/**
  * This method performs a financial transaction against this payment object.
  * 
  * @throws jmsPaymentApprovalExpiredException
  * @throws jmsPaymentCommunicationException
  * @throws jmsPaymentFinancialException
  * @throws jmsPaymentFunctionNotSupportedException
  * @throws jmsPaymentTimeoutException
  * @throws jmsPaymentUserActionRequiredException
  * @param mixed $transaction One of the FinancialTransaction::TYPE_* constants,
  *                           or an instance of FinancialTransaction
  * @return void
  */
 public final function performTransaction($transaction)
 {
     if (is_string($transaction)) {
         $transaction = FinancialTransaction::create($transaction, $this);
     }
     if (!$transaction instanceof FinancialTransaction) {
         throw new InvalidArgumentException(sprintf('"%s" must inherit from FinancialTransaction.', get_class($transaction)));
     }
     // for now, only transactions which are NEW or PENDING can be executed
     // against this payment
     if ($transaction->isInFinalState() === true) {
         throw new LogicException('The given transaction is already in a final state.');
     }
     // check if this payment is in a final state. If so, no transactions can be
     // performed against it.
     if ($this->isInFinalState()) {
         throw new LogicException('This payment is in a final state; no transactions' . ' can be performed against it.');
     }
     // verify that the transaction belongs to this payment
     if ($transaction->Payment !== $this) {
         throw new InvalidArgumentException('The given transaction already belongs to another payment.');
     }
     $event = $this->dispatchEvent(new jmsPaymentTransactionEvent(self::EVENT_PRE_TRANSACTION, $this, $transaction));
     if ($event->isPreventDefault() === true) {
         $transaction->state = FinancialTransaction::STATE_CANCELED;
         $transaction->save();
         throw new jmsPaymentFinancialException('The transaction was prevented by one of the listeners.');
     }
     $transaction->execute();
     // TODO: Consider dispatching an event for transactions which do not
     //       complete successfully. Maybe split into EVENT_POST_SUCCESSFUL_TRANSACTION
     //       and EVENT_POST_UNSUCCESSFUL_TRANSACTION?
     $this->dispatchEvent(new jmsPaymentTransactionEvent(self::EVENT_POST_TRANSACTION, $this, $transaction));
 }