/**
  * This should be called each time there may have been an update to a
  * payment on a transaction (ie, we asked for a payment to process a
  * payment for a transaction, or we told a payment method about an IPN, or
  * we told a payment method to
  * "finalize_payment_for" (a transaction), or we told a payment method to
  * process a refund. This should handle firing the correct hooks to
  * indicate
  * what exactly happened and updating the transaction appropriately). This
  * could be integrated directly into EE_Transaction upon save, but we want
  * this logic to be separate from 'normal' plain-jane saving and updating
  * of transactions and payments, and to be tied to payment processing.
  * Note: this method DOES NOT save the payment passed into it. It is the responsibility
  * of previous code to decide whether or not to save (because the payment passed into
  * this method might be a temporary, never-to-be-saved payment from an offline gateway,
  * in which case we only want that payment object for some temporary usage during this request,
  * but we don't want it to be saved).
  *
  * @param EE_Transaction|int $transaction
  * @param EE_Payment     $payment
  * @param boolean        $update_txn
  *                        whether or not to call
  *                        EE_Transaction_Processor::
  *                        update_transaction_and_registrations_after_checkout_or_payment()
  *                        (you can save 1 DB query if you know you're going
  *                        to save it later instead)
  * @param bool           $IPN
  *                        if processing IPNs or other similar payment
  *                        related activities that occur in alternate
  *                        requests than the main one that is processing the
  *                        TXN, then set this to true to check whether the
  *                        TXN is locked before updating
  * @throws \EE_Error
  */
 public function update_txn_based_on_payment($transaction, $payment, $update_txn = true, $IPN = false)
 {
     $do_action = 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__not_successful';
     /** @type EE_Transaction $transaction */
     $transaction = EEM_Transaction::instance()->ensure_is_obj($transaction);
     // can we freely update the TXN at this moment?
     if ($IPN && $transaction->is_locked()) {
         // don't update the transaction at this exact moment
         // because the TXN is active in another request
         EE_Cron_Tasks::schedule_update_transaction_with_payment(time(), $transaction->ID(), $payment->ID());
     } else {
         // verify payment and that it has been saved
         if ($payment instanceof EE_Payment && $payment->ID()) {
             if ($payment->payment_method() instanceof EE_Payment_Method && $payment->payment_method()->type_obj() instanceof EE_PMT_Base) {
                 $payment->payment_method()->type_obj()->update_txn_based_on_payment($payment);
                 // update TXN registrations with payment info
                 $this->process_registration_payments($transaction, $payment);
             }
             $do_action = $payment->just_approved() ? 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__successful' : $do_action;
         } else {
             // send out notifications
             add_filter('FHEE__EED_Messages___maybe_registration__deliver_notifications', '__return_true');
             $do_action = 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__no_payment_made';
         }
         // if this is an IPN, then we want to know the initial TXN status prior to updating the TXN
         // so that we know whether the status has changed and notifications should be triggered
         if ($IPN) {
             /** @type EE_Transaction_Processor $transaction_processor */
             $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
             $transaction_processor->set_old_txn_status($transaction->status_ID());
         }
         if ($payment->status() !== EEM_Payment::status_id_failed) {
             /** @type EE_Transaction_Payments $transaction_payments */
             $transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
             // set new value for total paid
             $transaction_payments->calculate_total_payments_and_update_status($transaction);
             // call EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment() ???
             if ($update_txn) {
                 $this->_post_payment_processing($transaction, $payment, $IPN);
             }
         }
         // granular hook for others to use.
         do_action($do_action, $transaction, $payment);
         do_action('AHEE_log', __CLASS__, __FUNCTION__, $do_action, '$do_action');
         //global hook for others to use.
         do_action('AHEE__EE_Payment_Processor__update_txn_based_on_payment', $transaction, $payment);
     }
 }
 /**
  * finalize_abandoned_transactions
  *
  * loops through the self::$_abandoned_transactions array
  * and attempts to finalize any TXNs that have not been completed
  * but have had their sessions expired, most likely due to a user not
  * returning from an off-site payment gateway
  */
 public static function finalize_abandoned_transactions()
 {
     // are there any TXNs that need cleaning up ?
     if (!empty(self::$_abandoned_transactions)) {
         /** @type EE_Transaction_Processor $transaction_processor */
         $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
         // set revisit flag for txn processor
         $transaction_processor->set_revisit(false);
         /** @type EE_Transaction_Payments $transaction_payments */
         $transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
         // load EEM_Transaction
         EE_Registry::instance()->load_model('Transaction');
         foreach (self::$_abandoned_transactions as $TXN_ID) {
             // reschedule the cron if we can't hit the db right now
             if (!EE_Maintenance_Mode::instance()->models_can_query()) {
                 // reset cron job for finalizing the TXN
                 EE_Cron_Tasks::schedule_finalize_abandoned_transactions_check(time() + 10 * MINUTE_IN_SECONDS + 1, $TXN_ID);
                 continue;
             }
             $transaction = EEM_Transaction::instance()->get_one_by_ID($TXN_ID);
             // verify transaction
             if ($transaction instanceof EE_Transaction) {
                 // or have had all of their reg steps completed
                 if ($transaction_processor->all_reg_steps_completed($transaction) === true) {
                     continue;
                 }
                 // maybe update status, but don't save transaction just yet
                 $transaction_payments->update_transaction_status_based_on_total_paid($transaction, false);
                 // check if enough Reg Steps have been completed to warrant finalizing the TXN
                 if ($transaction_processor->all_reg_steps_completed_except_final_step($transaction)) {
                     // and if it hasn't already been set as being started...
                     $transaction_processor->set_reg_step_initiated($transaction, 'finalize_registration');
                 }
                 // now update the TXN and trigger notifications
                 $transaction_processor->update_transaction_and_registrations_after_checkout_or_payment($transaction, $transaction->last_payment());
             }
             unset(self::$_abandoned_transactions[$TXN_ID]);
         }
     }
 }
 /**
  * _post_payment_processing
  *
  * @access private
  * @param EE_Payment $payment
  * @return bool
  */
 private function _post_payment_processing($payment = NULL)
 {
     // On-Site payment?
     if ($this->checkout->payment_method->is_on_site()) {
         if (!$this->_process_payment_status($payment, EE_PMT_Base::onsite)) {
             //$this->_setup_redirect_for_next_step();
             $this->checkout->continue_reg = false;
         }
         // Off-Site payment?
     } else {
         if ($this->checkout->payment_method->is_off_site()) {
             // if a payment object was made and it specifies a redirect url, then we'll setup that redirect info
             if ($payment instanceof EE_Payment && $payment->redirect_url()) {
                 do_action('AHEE_log', __CLASS__, __FUNCTION__, $payment->redirect_url(), '$payment->redirect_url()');
                 $this->checkout->redirect = TRUE;
                 $this->checkout->redirect_form = $payment->redirect_form();
                 $this->checkout->redirect_url = $this->reg_step_url('redirect_form');
                 // set JSON response
                 $this->checkout->json_response->set_redirect_form($this->checkout->redirect_form);
                 // set cron job for finalizing the TXN
                 // in case the user does not return from the off-site gateway
                 EE_Cron_Tasks::schedule_finalize_abandoned_transactions_check(EE_Registry::instance()->SSN->expiration() + 1, $this->checkout->transaction->ID());
                 // and lastly, let's bump the payment status to pending
                 $payment->set_status(EEM_Payment::status_id_pending);
                 $payment->save();
             } else {
                 // not a payment
                 $this->checkout->continue_reg = false;
                 EE_Error::add_error(sprintf(__('It appears the Off Site Payment Method was not configured properly.%sPlease try again or contact %s for assistance.', 'event_espresso'), '<br/>', EE_Registry::instance()->CFG->organization->get_pretty('email')), __FILE__, __FUNCTION__, __LINE__);
             }
             // Off-Line payment?
         } else {
             if ($payment === TRUE) {
                 //$this->_setup_redirect_for_next_step();
                 return TRUE;
             } else {
                 $this->checkout->continue_reg = false;
                 return FALSE;
             }
         }
     }
     return $payment;
 }
 /**
  * finalize_abandoned_transactions
  * loops through the self::$_abandoned_transactions array
  * and attempts to finalize any TXNs that have not been completed
  * but have had their sessions expired, most likely due to a user not
  * returning from an off-site payment gateway
  *
  * @throws \EE_Error
  */
 public static function finalize_abandoned_transactions()
 {
     do_action('AHEE_log', __CLASS__, __FUNCTION__);
     // are there any TXNs that need cleaning up ?
     if (!empty(self::$_abandoned_transactions)) {
         /** @type EE_Transaction_Processor $transaction_processor */
         $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
         // set revisit flag for txn processor
         $transaction_processor->set_revisit(false);
         /** @type EE_Payment_Processor $payment_processor */
         $payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
         // load EEM_Transaction
         EE_Registry::instance()->load_model('Transaction');
         foreach (self::$_abandoned_transactions as $TXN_ID) {
             do_action('AHEE_log', __CLASS__, __FUNCTION__, $TXN_ID, '$TXN_ID');
             // reschedule the cron if we can't hit the db right now
             if (!EE_Maintenance_Mode::instance()->models_can_query()) {
                 // reset cron job for finalizing the TXN
                 EE_Cron_Tasks::schedule_finalize_abandoned_transactions_check(time() + EE_Cron_Tasks::reschedule_timeout, $TXN_ID);
                 continue;
             }
             $transaction = EEM_Transaction::instance()->get_one_by_ID($TXN_ID);
             // verify transaction
             if ($transaction instanceof EE_Transaction) {
                 // don't finalize the TXN if it has already been completed
                 if ($transaction_processor->all_reg_steps_completed($transaction) === true) {
                     continue;
                 }
                 // let's simulate an IPN here which will trigger any notifications that need to go out
                 $payment_processor->update_txn_based_on_payment($transaction, $transaction->last_payment(), true, true);
             }
             unset(self::$_abandoned_transactions[$TXN_ID]);
         }
     }
 }