/**
  * Process a transaction.
  *
  * <code>
  * $context = "com_crowdfunding.transaction.process";
  *
  * $data = array(
  *    'txn_id' => 'TXN123456',
  *    'txn_amount' => '100',
  *    'txn_currency' => 'EUR',
  * //....
  * );
  *
  * // Create user transaction object based.
  * $transaction  = new Crowdfunding\Transaction\Transaction(\JFactory::getDbo());
  * $transaction->bind($data);
  *
  * $transactionManager = new Crowdfunding\Transaction\TransactionManager(\JFactory::getDbo());
  * $transactionManager->setTransaction($transaction);
  *
  * if ($transactionManager->process($context, $options)) {
  * // ...
  * }
  * </code>
  *
  * @param string $context
  * @param array $options
  *
  * @throws \RuntimeException
  */
 public function process($context, array $options = array())
 {
     if (!$this->transaction instanceof Transaction) {
         throw new \UnexpectedValueException('It is missing transaction object.');
     }
     // Implement JObservableInterface: Pre-processing by observers
     $this->observers->update('onBeforeProcessTransaction', array($context, &$this->transaction, &$options));
     $this->transaction->store();
     // Implement JObservableInterface: Post-processing by observers
     $this->observers->update('onAfterProcessTransaction', array($context, &$this->transaction, &$options));
 }
 /**
  * Save transaction data.
  *
  * @param array     $transactionData
  *
  * @throws \RuntimeException
  * @throws \InvalidArgumentException
  * @throws \UnexpectedValueException
  *
  * @return Transaction|null
  */
 protected function storeTransaction($transactionData)
 {
     // Get transaction object by transaction ID
     $keys = array('txn_id' => ArrayHelper::getValue($transactionData, 'txn_id'));
     $transaction = new Transaction(JFactory::getDbo());
     $transaction->load($keys);
     // DEBUG DATA
     JDEBUG ? $this->log->add(JText::_($this->textPrefix . '_DEBUG_TRANSACTION_OBJECT'), $this->debugType, $transaction->getProperties()) : null;
     // Check for existed transaction
     // If the current status if completed, stop the payment process.
     if ($transaction->getId() and $transaction->isCompleted()) {
         return null;
     }
     // Add extra data.
     if (array_key_exists('extra_data', $transactionData)) {
         if (!empty($transactionData['extra_data'])) {
             $transaction->addExtraData($transactionData['extra_data']);
         }
         unset($transactionData['extra_data']);
     }
     // IMPORTANT: It must be before ->bind();
     $options = array('old_status' => $transaction->getStatus(), 'new_status' => $transactionData['txn_status']);
     // Create the new transaction record if there is not record.
     // If there is new record, store new data with new status.
     // Example: It has been 'pending' and now is 'completed'.
     // Example2: It has been 'pending' and now is 'failed'.
     $transaction->bind($transactionData);
     // Start database transaction.
     $db = JFactory::getDbo();
     $db->transactionStart();
     try {
         $transactionManager = new TransactionManager($db);
         $transactionManager->setTransaction($transaction);
         $transactionManager->process('com_crowdfunding.payment', $options);
     } catch (Exception $e) {
         $db->transactionRollback();
         $this->log->add(JText::_($this->textPrefix . '_ERROR_TRANSACTION_PROCESS'), $this->errorType, $e->getMessage());
         return null;
     }
     // Commit database transaction.
     $db->transactionCommit();
     return $transaction;
 }
 /**
  * Remove an intention records.
  *
  * @param PaymentSessionRemote $paymentSession
  * @param Transaction $transaction
  */
 protected function removeIntention(PaymentSessionRemote $paymentSession, Transaction $transaction)
 {
     // Remove intention record.
     $removeIntention = (strcmp('completed', $transaction->getStatus()) === 0 or strcmp('pending', $transaction->getStatus()) === 0);
     if ($paymentSession->getIntentionId() and $removeIntention) {
         $intention = new Crowdfunding\Intention(\JFactory::getDbo());
         $intention->load($paymentSession->getIntentionId());
         if ($intention->getId()) {
             $intention->delete();
         }
     }
 }
 /**
  * Decrease the number of distributed to a user rewards.
  *
  * @param Transaction $transaction
  * @param Reward|null $reward
  *
  * @throws  \RuntimeException
  * @throws  \InvalidArgumentException
  * @throws  \UnexpectedValueException
  *
  * @return void
  */
 protected function decreaseDistributedReward(Transaction $transaction, $reward)
 {
     // Check for valid reward.
     if ($reward === null or !$reward->getId()) {
         return;
     }
     // Check for valida amount between reward value and payed by user
     $txnAmount = $transaction->getAmount();
     if ($txnAmount < $reward->getAmount()) {
         return;
     }
     // Decrease the number of distributed rewards.
     $reward->decreaseDistributed();
     $reward->updateDistributed();
 }