/** * 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]); } } }