public function setUp()
 {
     EEM_Payment::reset();
     EEM_Transaction::reset();
     //		EEM_Payment_Method::reset();
     parent::setUp();
 }
 protected function _get_data_for_context($context, EE_Registration $registration, $id)
 {
     //use the registration to get the transaction.
     $transaction = $registration->transaction();
     //bail early if no transaction
     if (!$transaction instanceof EE_Transaction) {
         throw new EE_Error(__('The given registration does not have an associated transaction. Something is wrong.', 'event_espresso'));
     }
     $payment = !empty($id) ? EEM_Payment::instance()->get_one(array(array('PAY_ID' => $id, 'TXN_ID' => $transaction->ID()))) : 0;
     return array($transaction, $payment);
 }
 /**
  *		This function is a singleton method used to instantiate the EEM_Payment object
  *
  *		@access public
  *		@param string $timezone string representing the timezone we want to set for returned Date Time Strings (and any incoming timezone data that gets saved).  Note this just sends the timezone info to the date time model field objects.  Default is NULL (and will be assumed using the set timezone in the 'timezone_string' wp option)
  *		@return EEM_Payment instance
  */
 public static function instance($timezone = NULL)
 {
     // check if instance of EEM_Payment already exists
     if (self::$_instance === NULL) {
         // instantiate Espresso_model
         self::$_instance = new self($timezone);
     }
     //we might have a timezone set, let set_timezone decide what to do with it
     self::$_instance->set_timezone($timezone);
     // EEM_Payment object
     return self::$_instance;
 }
 /**
  * @since 4.6.0
  */
 public function test_get_payments_between_dates()
 {
     $timezone = new DateTimeZone('America/Toronto');
     //set $now in the timezone being tested.
     $now = new DateTime('now', $timezone);
     $this->_setup_payments($now, $timezone);
     //test defaults
     $this->assertEquals(2, count(EEM_Payment::instance()->get_payments_made_between_dates()));
     //test including a date from past date for start date.
     $this->assertEquals(4, count(EEM_Payment::instance()->get_payments_made_between_dates($now->sub(new DateInterval('P2D'))->format('d/m/Y'), '', 'd/m/Y', 'America/Toronto')));
     //test including a date from past date for end date.
     $this->assertEquals(4, count(EEM_Payment::instance()->get_payments_made_between_dates('', $now->format('d/m/Y'), 'd/m/Y', 'America/Toronto')));
     //test including a date from upcoming date for start date
     $this->assertEquals(3, count(EEM_Payment::instance()->get_payments_made_between_dates($now->add(new DateInterval('P4D'))->format('d/m/Y'), '', 'd/m/Y', 'America/Toronto')));
     //test including a date from upcoming date for end date
     $this->assertEquals(3, count(EEM_Payment::instance()->get_payments_made_between_dates('', $now->format('d/m/Y'), 'd/m/Y', 'America/Toronto')));
     //test exception
     $this->setExpectedException('EE_Error');
     EEM_Payment::instance()->get_payments_made_between_dates('trigger_exception');
 }
 public function test_just_approved()
 {
     $p1 = EE_Payment::new_instance();
     $this->assertFalse($p1->just_approved());
     $p1->set_status(EEM_Payment::status_id_approved);
     $this->assertTrue($p1->just_approved());
     $id = $p1->save();
     $this->assertTrue($p1->just_approved());
     EEM_Payment::reset();
     //now try with a payment that began as approved
     //note that we've reset EEM_payment so this is just like
     //it had been created on a previous request
     $p2 = EEM_Payment::instance()->get_one_by_ID($id);
     $this->assertFalse($p2->just_approved());
     $p2->set_status(EEM_Payment::status_id_pending);
     $p2->save();
     EEM_Payment::reset();
     //again, pretend this next part is a subsequent request
     $p3 = EEM_Payment::instance()->get_one_by_ID($id);
     $this->assertFalse($p3->just_approved());
     $p3->set_status(EEM_Payment::status_id_approved);
     $this->assertTrue($p3->just_approved());
 }
 /**
  * Updates the transaction according to the payment info
  * @param EE_Transaction or int $transaction the transaction to update, or its ID. Cannot be null.
  * @param EE_Payment or int $payment the payment just made or its ID. If empty that's actually OK. It just means no payment has been made.
  * @return boolean success
  */
 public function update_transaction_with_payment($transaction, $payment)
 {
     if (empty($transaction)) {
         return false;
     }
     $transaction = $this->_TXN->ensure_is_obj($transaction);
     /* @var $transaction EE_transaction */
     //now, if the payment's empty, we're going to update the transaction accordingly
     if (empty($payment)) {
         $transaction->set_status(EEM_Transaction::incomplete_status_code);
         $transaction->update_extra_meta('gateway', $this->_gateway_name);
         do_action('AHEE__EE_Gateway__update_transaction_with_payment__no_payment', $transaction);
     } else {
         $payment = $this->_PAY->ensure_is_obj($payment);
         //ok, now process the transaction according to the payment
         $transaction->update_based_on_payments();
         $transaction->update_extra_meta('gateway', $this->_gateway_name);
         do_action('AHEE__EE_Gateway__update_transaction_with_payment__done', $transaction, $payment);
     }
     $transaction->save();
     $transaction->finalize();
     return true;
 }
 /**
  *
  * @param EE_Transaction       $transaction
  * @param float                $amount
  * @param EE_Billing_Info_Form $billing_info
  * @param string               $return_url
  * @param string                 $fail_url
  * @param string               $method
  * @param bool           $by_admin
  * @return EE_Payment
  * @throws EE_Error
  */
 function process_payment(EE_Transaction $transaction, $amount = null, $billing_info = null, $return_url = null, $fail_url = '', $method = 'CART', $by_admin = false)
 {
     // @todo: add surcharge for the payment method, if any
     if ($this->_gateway) {
         //there is a gateway, so we're going to make a payment object
         //but wait! do they already have a payment in progress that we thought was failed?
         $duplicate_properties = array('STS_ID' => EEM_Payment::status_id_failed, 'TXN_ID' => $transaction->ID(), 'PMD_ID' => $this->_pm_instance->ID(), 'PAY_source' => $method, 'PAY_amount' => $amount !== null ? $amount : $transaction->remaining(), 'PAY_gateway_response' => null);
         $payment = EEM_Payment::instance()->get_one(array($duplicate_properties));
         //if we didn't already have a payment in progress for the same thing,
         //then we actually want to make a new payment
         if (!$payment instanceof EE_Payment) {
             $payment = EE_Payment::new_instance(array_merge($duplicate_properties, array('PAY_timestamp' => time(), 'PAY_txn_id_chq_nmbr' => null, 'PAY_po_number' => null, 'PAY_extra_accntng' => null, 'PAY_details' => null)));
         }
         //make sure the payment has been saved to show we started it, and so it has an ID should the gateway try to log it
         $payment->save();
         $billing_values = $this->_get_billing_values_from_form($billing_info);
         //  Offsite Gateway
         if ($this->_gateway instanceof EE_Offsite_Gateway) {
             $payment = $this->_gateway->set_redirection_info($payment, $billing_values, $return_url, EE_Config::instance()->core->txn_page_url(array('e_reg_url_link' => $transaction->primary_registration()->reg_url_link(), 'ee_payment_method' => $this->_pm_instance->slug())), $fail_url);
             $payment->save();
             //  Onsite Gateway
         } elseif ($this->_gateway instanceof EE_Onsite_Gateway) {
             $payment = $this->_gateway->do_direct_payment($payment, $billing_values);
             $payment->save();
         } else {
             throw new EE_Error(sprintf(__('Gateway for payment method type "%s" is "%s", not a subclass of either EE_Offsite_Gateway or EE_Onsite_Gateway, or null (to indicate NO gateway)', 'event_espresso'), get_class($this), gettype($this->_gateway)));
         }
     } else {
         // no gateway provided
         // there is no payment. Must be an offline gateway
         // create a payment object anyways, but dont save it
         $payment = EE_Payment::new_instance(array('STS_ID' => EEM_Payment::status_id_pending, 'TXN_ID' => $transaction->ID(), 'PMD_ID' => $transaction->payment_method_ID(), 'PAY_amount' => 0.0, 'PAY_timestamp' => time()));
     }
     // if there is billing info, clean it and save it now
     if ($billing_info instanceof EE_Billing_Attendee_Info_Form) {
         $this->_save_billing_info_to_attendee($billing_info, $transaction);
     }
     return $payment;
 }
 /**
  * Process the IPN. Firstly, we'll hope we put the standard args into the IPN URL so
  * we can easily find what registration the IPN is for and what payment method.
  * However, if not, we'll give all payment methods a chance to claim it and process it.
  * If a payment is found for the IPN info, it is saved.
  *
  * @param array             $_req_data eg $_REQUEST
  * @param EE_Transaction|int $transaction          optional (or a transactions id)
  * @param EE_Payment_Method $payment_method       (or a slug or id of one)
  * @param boolean           $update_txn           whether or not to call
  *                                                EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()
  * @param bool              $separate_IPN_request whether the IPN uses a separate request ( true like PayPal )
  *                                                or is processed manually ( false like Mijireh )
  * @throws EE_Error
  * @throws Exception
  * @return EE_Payment
  */
 public function process_ipn($_req_data, $transaction = null, $payment_method = null, $update_txn = true, $separate_IPN_request = true)
 {
     EE_Registry::instance()->load_model('Change_Log');
     $_req_data = $this->_remove_unusable_characters_from_array((array) $_req_data);
     EE_Processor_Base::set_IPN($separate_IPN_request);
     $obj_for_log = null;
     if ($transaction instanceof EE_Transaction) {
         $obj_for_log = $transaction;
         if ($payment_method instanceof EE_Payment_Method) {
             $obj_for_log = EEM_Payment::instance()->get_one(array(array('TXN_ID' => $transaction->ID(), 'PMD_ID' => $payment_method->ID()), 'order_by' => array('PAY_timestamp' => 'desc')));
         }
     } else {
         if ($payment_method instanceof EE_Payment) {
             $obj_for_log = $payment_method;
         }
     }
     $log = EEM_Change_Log::instance()->log(EEM_Change_Log::type_gateway, array('IPN data received' => $_req_data), $obj_for_log);
     try {
         /**
          * @var EE_Payment $payment
          */
         $payment = NULL;
         if ($transaction && $payment_method) {
             /** @type EE_Transaction $transaction */
             $transaction = EEM_Transaction::instance()->ensure_is_obj($transaction);
             /** @type EE_Payment_Method $payment_method */
             $payment_method = EEM_Payment_Method::instance()->ensure_is_obj($payment_method);
             if ($payment_method->type_obj() instanceof EE_PMT_Base) {
                 $payment = $payment_method->type_obj()->handle_ipn($_req_data, $transaction);
                 $log->set_object($payment);
             } else {
                 // not a payment
                 EE_Error::add_error(sprintf(__('A valid payment method could not be determined due to a technical issue.%sPlease refresh your browser and try again or contact %s for assistance.', 'event_espresso'), '<br/>', EE_Registry::instance()->CFG->organization->get_pretty('email')), __FILE__, __FUNCTION__, __LINE__);
             }
         } else {
             //that's actually pretty ok. The IPN just wasn't able
             //to identify which transaction or payment method this was for
             // give all active payment methods a chance to claim it
             $active_payment_methods = EEM_Payment_Method::instance()->get_all_active();
             foreach ($active_payment_methods as $active_payment_method) {
                 try {
                     $payment = $active_payment_method->type_obj()->handle_unclaimed_ipn($_req_data);
                     $payment_method = $active_payment_method;
                     EEM_Change_Log::instance()->log(EEM_Change_Log::type_gateway, array('IPN data' => $_req_data), $payment);
                     break;
                 } catch (EE_Error $e) {
                     //that's fine- it apparently couldn't handle the IPN
                 }
             }
         }
         // 			EEM_Payment_Log::instance()->log("got to 7",$transaction,$payment_method);
         if ($payment instanceof EE_Payment) {
             $payment->save();
             //  update the TXN
             $this->update_txn_based_on_payment($transaction, $payment, $update_txn, $separate_IPN_request);
         } else {
             //we couldn't find the payment for this IPN... let's try and log at least SOMETHING
             if ($payment_method) {
                 EEM_Change_Log::instance()->log(EEM_Change_Log::type_gateway, array('IPN data' => $_req_data), $payment_method);
             } elseif ($transaction) {
                 EEM_Change_Log::instance()->log(EEM_Change_Log::type_gateway, array('IPN data' => $_req_data), $transaction);
             }
         }
         return $payment;
     } catch (EE_Error $e) {
         do_action('AHEE__log', __FILE__, __FUNCTION__, sprintf(__('Error occurred while receiving IPN. Transaction: %1$s, req data: %2$s. The error was "%3$s"', 'event_espresso'), print_r($transaction, TRUE), print_r($_req_data, TRUE), $e->getMessage()));
         throw $e;
     }
 }
 /**
  * delete_payment
  * 	delete a payment or refund made towards a transaction
  *
  * @access public
  *	@return void
  */
 public function delete_payment()
 {
     $json_response_data = array('return_data' => FALSE);
     $PAY_ID = isset($this->_req_data['delete_txn_admin_payment'], $this->_req_data['delete_txn_admin_payment']['PAY_ID']) ? absint($this->_req_data['delete_txn_admin_payment']['PAY_ID']) : 0;
     if ($PAY_ID) {
         $delete_txn_reg_status_change = isset($this->_req_data['delete_txn_reg_status_change']) ? $this->_req_data['delete_txn_reg_status_change'] : false;
         $payment = EEM_Payment::instance()->get_one_by_ID($PAY_ID);
         if ($payment instanceof EE_Payment) {
             $REG_IDs = $this->_get_existing_reg_payment_REG_IDs($payment);
             /** @type EE_Transaction_Payments $transaction_payments */
             $transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
             if ($transaction_payments->delete_payment_and_update_transaction($payment)) {
                 EE_Error::add_success(__('The Payment was successfully deleted.', 'event_espresso'));
                 $json_response_data['return_data'] = array('PAY_ID' => $PAY_ID, 'amount' => $payment->amount(), 'total_paid' => $payment->transaction()->paid(), 'txn_status' => $payment->transaction()->status_ID(), 'pay_status' => $payment->STS_ID(), 'delete_txn_reg_status_change' => $delete_txn_reg_status_change);
                 //if non empty reg_ids lets get an array of registrations and update the values for the apply_payment/refund rows.
                 if (!empty($REG_IDs)) {
                     EE_Registry::instance()->load_helper('Template');
                     $registrations = EEM_Registration::instance()->get_all(array(array('REG_ID' => array('IN', $REG_IDs))));
                     foreach ($registrations as $registration) {
                         $json_response_data['return_data']['registrations'][$registration->ID()] = array('owing' => EEH_Template::format_currency($registration->final_price() - $registration->paid()), 'paid' => $registration->pretty_paid());
                     }
                 }
                 if ($delete_txn_reg_status_change) {
                     $this->_req_data['txn_reg_status_change'] = $delete_txn_reg_status_change;
                     //MAKE sure we also add the delete_txn_req_status_change to the
                     //$_REQUEST global because that's how messages will be looking
                     //for it.
                     $_REQUEST['txn_reg_status_change'] = $delete_txn_reg_status_change;
                     $this->_process_registration_status_change($payment->transaction());
                 }
             }
         } else {
             EE_Error::add_error(__('Valid Payment data could not be retrieved from the database.', 'event_espresso'), __FILE__, __FUNCTION__, __LINE__);
         }
     } else {
         $msg = __('A valid Payment ID was not received, therefore payment form data could not be loaded.', 'event_espresso');
         EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
     }
     $notices = EE_Error::get_notices(FALSE, FALSE, FALSE);
     echo json_encode(array_merge($json_response_data, $notices));
     die;
 }
 /**
  * Data that has been stored in persistent storage that was prepped by _convert_data_for_persistent_storage
  * can be sent into this method and converted back into the format used for instantiating with this data handler.
  *
  * @param array  $data
  *
  * @return array
  */
 public static function convert_data_from_persistent_storage($data)
 {
     $prepped_data = array(0 => isset($data['Transaction']) ? EEM_Transaction::instance()->get_one_by_ID($data['Transaction']) : null, 1 => isset($data['Payment']) ? EEM_Payment::instance()->get_one_by_ID($data['Payment']) : null, 2 => isset($data['filter']) ? $data['filter'] : null);
     return $prepped_data;
 }
 /**
  * recalculate_total_payments_for_transaction
  *
  * @access public
  * @param EE_Transaction $transaction
  * @param string         $payment_status One of EEM_Payment's statuses, like 'PAP' (Approved).
  *                                       By default, searches for approved payments
  * @return float|false   float on success, false on fail
  * @throws \EE_Error
  */
 public function recalculate_total_payments_for_transaction(EE_Transaction $transaction, $payment_status = EEM_Payment::status_id_approved)
 {
     // verify transaction
     if (!$transaction instanceof EE_Transaction) {
         EE_Error::add_error(__('Please provide a valid EE_Transaction object.', 'event_espresso'), __FILE__, __FUNCTION__, __LINE__);
         return false;
     }
     // ensure Payment model is loaded
     EE_Registry::instance()->load_model('Payment');
     // calls EEM_Base::sum()
     return EEM_Payment::instance()->sum(array(array('TXN_ID' => $transaction->ID(), 'STS_ID' => $payment_status)), 'PAY_amount');
 }
 /**
  * @group 7151
  */
 function test_fresh_entity_map_with()
 {
     $p = $this->new_model_obj_with_dependencies('Payment', array('PAY_amount' => 25));
     $p->save();
     $this->assertEquals($p, EEM_Payment::instance()->get_from_entity_map($p->ID()));
     //now purposefully make a naughty payment which isn't in the entity map
     $p2 = clone $p;
     $this->assertFalse($p2->in_entity_map());
     //make the two EE_Payments diverge
     $p2->set('PAY_amount', 99);
     $t = EE_Transaction::new_instance();
     $p2->cache('Transaction', $t);
     $this->assertEquals(25, $p->get('PAY_amount'));
     $this->assertEquals(99, $p2->get('PAY_amount'));
     $this->assertNotEquals($p->get_all_from_cache('Transaction'), $p2->get_all_from_cache('Transaction'));
     //now update the payment in the entity map with the other
     EEM_Payment::instance()->refresh_entity_map_with($p->ID(), $p2);
     $this->assertEquals(99, $p->get('PAY_amount'));
     //make sure p hasn't changed into p2. that's not what we wanted to do...
     $this->assertFalse($p2 === $p);
     //We wanted to just UPDATE p with p2's values
     $this->assertEquals($p, EEM_Payment::instance()->get_from_entity_map($p->ID()));
     //and make sure p's cache was updated to be the same as p2's
     $this->assertEquals($p2->get_all_from_cache('Transaction'), $p->get_all_from_cache('Transaction'));
 }
 /**
  * test_build_payment_json_response_for_payment
  * @since    4.8
  * @group    8620
  */
 public function test_build_payment_json_response_for_payment()
 {
     $this->_admin_page = new Transactions_Admin_Page_Mock();
     $this->_setup_standard_transaction_and_payment(40.0, 4, 15.0);
     // need to make sure relation is set between payment and payment method
     $this->_payment->_add_relation_to($this->_payment_method(), 'Payment_Method');
     $registrations = $this->_get_x_number_of_registrations_from_transaction($this->_transaction, 1);
     $this->_apply_payment_to_registrations($registrations);
     $json_response_data = $this->_admin_page->build_payment_json_response($this->_payment);
     $pay_status = EEM_Payment::instance()->status_array(true);
     $this->assertEquals(15.0, $json_response_data['amount']);
     // total paid is still zero, because we haven't actually updated the TXN with the payment info
     $this->assertEquals(0, $json_response_data['total_paid']);
     $this->assertEquals(EEM_Transaction::incomplete_status_code, $json_response_data['txn_status']);
     $this->assertEquals(EEM_Payment::status_id_approved, $json_response_data['pay_status']);
     $this->assertEquals(EEM_Payment::status_id_approved, $json_response_data['STS_ID']);
     $this->assertEquals($pay_status[EEM_Payment::status_id_approved], $json_response_data['status']);
     $this->assertEquals($this->_payment->ID(), $json_response_data['PAY_ID']);
     $this->assertEquals($this->_payment->timestamp('Y-m-d', 'h:i a'), $json_response_data['date']);
     $this->assertEquals(strtoupper($this->_payment->source()), $json_response_data['method']);
     $this->assertEquals($this->_payment_method()->ID(), $json_response_data['PM_ID']);
     $this->assertEquals($this->_payment_method()->admin_name(), $json_response_data['gateway']);
     $this->assertEquals($this->_payment->gateway_response(), $json_response_data['gateway_response']);
     $this->assertEquals($this->_payment->txn_id_chq_nmbr(), $json_response_data['txn_id_chq_nmbr']);
     $this->assertEquals($this->_payment->po_number(), $json_response_data['po_number']);
     $this->assertEquals($this->_payment->extra_accntng(), $json_response_data['extra_accntng']);
     $this->assertEquals($this->_payment->extra_accntng(), $json_response_data['extra_accntng']);
     // will validate $json_response_data[ 'registrations' ] in test_registration_payment_data_array()
 }
 /**
  * return the payment object for a given payment ID
  *
  * @since 4.3.0
  *
  * @param int $PMT_ID the payment id for the payment to attempt to retrieve
  *
  * @return mixed null|EE_Payment
  */
 public function get_object_by_id($PMT_ID)
 {
     return EEM_Payment::instance()->get_one_by_ID($PMT_ID);
 }
 public function test_process_payment__offsite__declined_then_approved()
 {
     /** @type EE_Payment_Method $pm */
     $pm = $this->new_model_obj_with_dependencies('Payment_Method', array('PMD_type' => 'Mock_Offsite'));
     $transaction = $this->_new_typical_transaction();
     global $wp_actions;
     EE_Registry::instance()->load_helper('Array');
     $successful_payment_actions = EEH_Array::is_set($wp_actions, 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__successful', 0);
     /** @type EE_Payment_Processor $payment_processor */
     $payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
     $payment = $payment_processor->process_payment($pm, $transaction, NULL, NULL, 'success', 'CART', TRUE, TRUE);
     $this->assertInstanceOf('EE_Payment', $payment);
     //verify it's already been saved
     $this->assertNotEquals(0, $payment->ID());
     //assert that the payment still has its default status
     $this->assertEquals(EEM_Payment::instance()->field_settings_for('STS_ID')->get_default_value(), $payment->status());
     $this->assertEquals(EEM_Payment::instance()->field_settings_for('STS_ID')->get_default_value(), $this->_get_payment_status_in_db($payment));
     //assert that the we haven't notified of successful payment JUST yet...
     $this->assertEquals($successful_payment_actions, EEH_Array::is_set($wp_actions, 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__successful', 0));
     //PENDING IPN
     $payment = $payment_processor->process_ipn(array('status' => EEM_Payment::status_id_pending, 'gateway_txn_id' => $payment->txn_id_chq_nmbr()), $transaction, $pm);
     $this->assertEquals(EEM_Payment::status_id_pending, $payment->status());
     $this->assertEquals(EEM_Payment::status_id_pending, $this->_get_payment_status_in_db($payment));
     //and the payment-approved action should have NOT been triggered
     $this->assertEquals($successful_payment_actions, EEH_Array::is_set($wp_actions, 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__successful', 0));
     //APPROVED IPN
     $payment = $payment_processor->process_ipn(array('status' => EEM_Payment::status_id_approved, 'gateway_txn_id' => $payment->txn_id_chq_nmbr()), $transaction, $pm);
     //payment should be what the gateway set it to be, which was approved
     $this->assertEquals(EEM_Payment::status_id_approved, $payment->status());
     $this->assertEquals(EEM_Payment::status_id_approved, $this->_get_payment_status_in_db($payment));
     //and the payment-approved action should have been triggered
     $this->assertEquals($successful_payment_actions + 1, EEH_Array::is_set($wp_actions, 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__successful', 0));
     //DUPLICATE SUCCESS IPN
     //for this, we need to reset payment model so we fetch a NEW payment object, instead of reusing the old
     //and because the payment method caches a payment method type which caches a gateway which caches the payment model,
     //we also need to reset the payment method
     EEM_Payment::reset();
     $pm = EEM_Payment_Method::reset()->get_one_by_ID($pm->ID());
     $payment = $payment_processor->process_ipn(array('status' => EEM_Payment::status_id_approved, 'gateway_txn_id' => $payment->txn_id_chq_nmbr()), $transaction, $pm);
     //payment should be what the gateway set it to be, which was failed
     $this->assertEquals(EEM_Payment::status_id_approved, $payment->status());
     //and the payment-approved action should have NOT been triggered this time because it's a duplicate
     $this->assertEquals($successful_payment_actions + 1, EEH_Array::is_set($wp_actions, 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__successful', 0));
 }
 /**
  * 		delete a payment or refund made towards a transaction
  *		@access public
  *		@return void
  */
 public function delete_payment()
 {
     $return_data = array();
     if (isset($this->_req_data['delete_txn_admin_payment'])) {
         $pymt = $this->_req_data['delete_txn_admin_payment'];
         if ($payment = EEM_Payment::instance()->get_one_by_ID($pymt['PAY_ID'])) {
             if ($transaction = EEM_Payment::instance()->delete_by_ID($payment->ID())) {
                 $return_data = array('amount' => $payment->amount(), 'total_paid' => $transaction->paid(), 'txn_status' => $transaction->status_ID(), 'pay_status' => $payment->STS_ID(), 'PAY_ID' => $pymt['PAY_ID'], 'delete_txn_reg_status_change' => $this->_req_data['delete_txn_reg_status_change']);
             }
         }
         if (isset($this->_req_data['delete_txn_reg_status_change'])) {
             $this->_req_data['txn_reg_status_change'] = $this->_req_data['delete_txn_reg_status_change'];
             $_REQUEST['txn_reg_status_change'] = $this->_req_data['delete_txn_reg_status_change'];
             $this->_process_registration_status_change($transaction);
         }
     } else {
         $msg = __('An error occurred. The payment form data could not be loaded.', 'event_espresso');
         EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
         $return_data = FALSE;
     }
     $notices = EE_Error::get_notices(FALSE, FALSE, FALSE);
     echo json_encode(array('return_data' => $return_data, 'success' => $notices['success'], 'errors' => $notices['errors']));
     die;
 }
 /**
  * update_transaction_with_payment
  *
  * 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 update_transaction_with_payment()
 {
     do_action('AHEE_log', __CLASS__, __FUNCTION__);
     // are there any TXNs that need cleaning up ?
     if (!empty(self::$_update_transactions_with_payment)) {
         /** @type EE_Payment_Processor $payment_processor */
         $payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
         // set revisit flag for payment processor
         $payment_processor->set_revisit(false);
         // load EEM_Transaction
         EE_Registry::instance()->load_model('Transaction');
         foreach (self::$_update_transactions_with_payment as $TXN_ID => $PAY_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 updating the TXN
                 EE_Cron_Tasks::schedule_update_transaction_with_payment(time() + EE_Cron_Tasks::reschedule_timeout, $TXN_ID, $PAY_ID);
                 continue;
             }
             $transaction = EEM_Transaction::instance()->get_one_by_ID($TXN_ID);
             $payment = EEM_Payment::instance()->get_one_by_ID($PAY_ID);
             // verify transaction
             if ($transaction instanceof EE_Transaction && $payment instanceof EE_Payment) {
                 // now try to update the TXN with any payments
                 $payment_processor->update_txn_based_on_payment($transaction, $payment, true, true);
             }
             unset(self::$_update_transactions_with_payment[$TXN_ID]);
         }
     }
 }
 /**
  * delete_payment
  * 	delete a payment or refund made towards a transaction
  *
  * @access public
  *	@return void
  */
 public function delete_payment()
 {
     $json_response_data = array('return_data' => FALSE);
     $PAY_ID = isset($this->_req_data['delete_txn_admin_payment'], $this->_req_data['delete_txn_admin_payment']['PAY_ID']) ? absint($this->_req_data['delete_txn_admin_payment']['PAY_ID']) : 0;
     if ($PAY_ID) {
         $delete_txn_reg_status_change = isset($this->_req_data['delete_txn_reg_status_change']) ? $this->_req_data['delete_txn_reg_status_change'] : false;
         $payment = EEM_Payment::instance()->get_one_by_ID($PAY_ID);
         if ($payment instanceof EE_Payment) {
             /** @type EE_Transaction_Payments $transaction_payments */
             $transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
             if ($transaction_payments->delete_payment_and_update_transaction($payment)) {
                 EE_Error::add_success(__('The Payment was successfully deleted.', 'event_espresso'));
                 $json_response_data['return_data'] = array('PAY_ID' => $PAY_ID, 'amount' => $payment->amount(), 'total_paid' => $payment->transaction()->paid(), 'txn_status' => $payment->transaction()->status_ID(), 'pay_status' => $payment->STS_ID(), 'delete_txn_reg_status_change' => $delete_txn_reg_status_change);
                 if ($delete_txn_reg_status_change) {
                     $this->_req_data['txn_reg_status_change'] = $delete_txn_reg_status_change;
                     //MAKE sure we also add the delete_txn_req_status_change to the
                     //$_REQUEST global because that's how messages will be looking
                     //for it.
                     $_REQUEST['txn_reg_status_change'] = $delete_txn_reg_status_change;
                     $this->_process_registration_status_change($payment->transaction());
                 }
             }
         } else {
             EE_Error::add_error(__('Valid Payment data could not be retrieved from the database.', 'event_espresso'), __FILE__, __FUNCTION__, __LINE__);
         }
     } else {
         $msg = __('A valid Payment ID was not received, therefore payment form data could not be loaded.', 'event_espresso');
         EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
     }
     $notices = EE_Error::get_notices(FALSE, FALSE, FALSE);
     echo json_encode(array_merge($json_response_data, $notices));
     die;
 }
 /**
  * Export a custom CSV of registration info including: A bunch of the reg fields, the time of the event, the price name,
  * and the questions associated with the registrations
  * @param int $event_id
  */
 function report_registrations_for_event($event_id = NULL)
 {
     $reg_fields_to_include = array('TXN_ID', 'ATT_ID', 'REG_ID', 'REG_date', 'REG_code', 'REG_count', 'REG_final_price');
     $att_fields_to_include = array('ATT_fname', 'ATT_lname', 'ATT_email', 'ATT_address', 'ATT_address2', 'ATT_city', 'STA_ID', 'CNT_ISO', 'ATT_zip', 'ATT_phone');
     $registrations_csv_ready_array = array();
     $reg_model = EE_Registry::instance()->load_model('Registration');
     $query_params = apply_filters('FHEE__EE_Export__report_registration_for_event', array(array('OR' => array('Transaction.STS_ID' => array('NOT IN', array(EEM_Transaction::failed_status_code, EEM_Transaction::abandoned_status_code)), 'STS_ID' => EEM_Registration::status_id_approved), 'Ticket.TKT_deleted' => array('IN', array(true, false))), 'order_by' => array('Transaction.TXN_ID' => 'asc', 'REG_count' => 'asc'), 'force_join' => array('Transaction', 'Ticket', 'Attendee'), 'caps' => EEM_Base::caps_read_admin), $event_id);
     if ($event_id) {
         $query_params[0]['EVT_ID'] = $event_id;
     } else {
         $query_params['force_join'][] = 'Event';
     }
     $registration_rows = $reg_model->get_all_wpdb_results($query_params);
     //get all questions which relate to someone in this group
     $registration_ids = array();
     foreach ($registration_rows as $reg_row) {
         $registration_ids[] = intval($reg_row['Registration.REG_ID']);
     }
     //		EEM_Question::instance()->show_next_x_db_queries();
     $questions_for_these_regs_rows = EEM_Question::instance()->get_all_wpdb_results(array(array('Answer.REG_ID' => array('IN', $registration_ids))));
     foreach ($registration_rows as $reg_row) {
         if (is_array($reg_row)) {
             $reg_csv_array = array();
             if (!$event_id) {
                 //get the event's name and Id
                 $reg_csv_array[__('Event', 'event_espresso')] = sprintf(__('%1$s (%2$s)', 'event_espresso'), $this->_prepare_value_from_db_for_display(EEM_Event::instance(), 'EVT_name', $reg_row['Event_CPT.post_title']), $reg_row['Event_CPT.ID']);
             }
             $is_primary_reg = $reg_row['Registration.REG_count'] == '1' ? true : false;
             /*@var $reg_row EE_Registration */
             foreach ($reg_fields_to_include as $field_name) {
                 $field = $reg_model->field_settings_for($field_name);
                 if ($field_name == 'REG_final_price') {
                     $value = $this->_prepare_value_from_db_for_display($reg_model, $field_name, $reg_row['Registration.REG_final_price'], 'localized_float');
                 } elseif ($field_name == 'REG_count') {
                     $value = sprintf(__('%s of %s', 'event_espresso'), $this->_prepare_value_from_db_for_display($reg_model, 'REG_count', $reg_row['Registration.REG_count']), $this->_prepare_value_from_db_for_display($reg_model, 'REG_group_size', $reg_row['Registration.REG_group_size']));
                 } elseif ($field_name == 'REG_date') {
                     $value = $this->_prepare_value_from_db_for_display($reg_model, $field_name, $reg_row['Registration.REG_date'], 'no_html');
                 } else {
                     $value = $this->_prepare_value_from_db_for_display($reg_model, $field_name, $reg_row[$field->get_qualified_column()]);
                 }
                 $reg_csv_array[$this->_get_column_name_for_field($field)] = $value;
                 if ($field_name == 'REG_final_price') {
                     //add a column named Currency after the final price
                     $reg_csv_array[__("Currency", "event_espresso")] = EE_Config::instance()->currency->code;
                 }
             }
             //get pretty status
             $stati = EEM_Status::instance()->localized_status(array($reg_row['Registration.STS_ID'] => __('unknown', 'event_espresso'), $reg_row['TransactionTable.STS_ID'] => __('unknown', 'event_espresso')), FALSE, 'sentence');
             $reg_csv_array[__("Registration Status", 'event_espresso')] = $stati[$reg_row['Registration.STS_ID']];
             //get pretty trnasaction status
             $reg_csv_array[__("Transaction Status", 'event_espresso')] = $stati[$reg_row['TransactionTable.STS_ID']];
             $reg_csv_array[__('Transaction Amount Due', 'event_espresso')] = $is_primary_reg ? $this->_prepare_value_from_db_for_display(EEM_Transaction::instance(), 'TXN_total', $reg_row['TransactionTable.TXN_total'], 'localized_float') : '0.00';
             $reg_csv_array[__('Amount Paid', 'event_espresso')] = $is_primary_reg ? $this->_prepare_value_from_db_for_display(EEM_Transaction::instance(), 'TXN_paid', $reg_row['TransactionTable.TXN_paid'], 'localized_float') : '0.00';
             $payment_methods = array();
             $gateway_txn_ids_etc = array();
             $payment_times = array();
             if ($is_primary_reg && $reg_row['TransactionTable.TXN_ID']) {
                 $payments_info = EEM_Payment::instance()->get_all_wpdb_results(array(array('TXN_ID' => $reg_row['TransactionTable.TXN_ID'], 'STS_ID' => EEM_Payment::status_id_approved), 'force_join' => array('Payment_Method')), ARRAY_A, 'Payment_Method.PMD_admin_name as name, Payment.PAY_txn_id_chq_nmbr as gateway_txn_id, Payment.PAY_timestamp as payment_time');
                 foreach ($payments_info as $payment_method_and_gateway_txn_id) {
                     $payment_methods[] = isset($payment_method_and_gateway_txn_id['name']) ? $payment_method_and_gateway_txn_id['name'] : __('Unknown', 'event_espresso');
                     $gateway_txn_ids_etc[] = isset($payment_method_and_gateway_txn_id['gateway_txn_id']) ? $payment_method_and_gateway_txn_id['gateway_txn_id'] : '';
                     $payment_times[] = isset($payment_method_and_gateway_txn_id['payment_time']) ? $payment_method_and_gateway_txn_id['payment_time'] : '';
                 }
             }
             $reg_csv_array[__('Payment Date(s)', 'event_espresso')] = implode(',', $payment_times);
             $reg_csv_array[__('Payment Method(s)', 'event_espresso')] = implode(",", $payment_methods);
             $reg_csv_array[__('Gateway Transaction ID(s)', 'event_espresso')] = implode(',', $gateway_txn_ids_etc);
             //get whether or not the user has checked in
             $reg_csv_array[__("Check-Ins", "event_espresso")] = $reg_model->count_related($reg_row['Registration.REG_ID'], 'Checkin');
             //get ticket of registration and its price
             $ticket_model = EE_Registry::instance()->load_model('Ticket');
             if ($reg_row['Ticket.TKT_ID']) {
                 $ticket_name = $this->_prepare_value_from_db_for_display($ticket_model, 'TKT_name', $reg_row['Ticket.TKT_name']);
                 $datetimes_strings = array();
                 foreach (EEM_Datetime::instance()->get_all_wpdb_results(array(array('Ticket.TKT_ID' => $reg_row['Ticket.TKT_ID']), 'order_by' => array('DTT_EVT_start' => 'ASC'), 'default_where_conditions' => 'none')) as $datetime) {
                     $datetimes_strings[] = $this->_prepare_value_from_db_for_display(EEM_Datetime::instance(), 'DTT_EVT_start', $datetime['Datetime.DTT_EVT_start']);
                 }
             } else {
                 $ticket_name = __('Unknown', 'event_espresso');
                 $datetimes_strings = array(__('Unknown', 'event_espresso'));
             }
             $reg_csv_array[$ticket_model->field_settings_for('TKT_name')->get_nicename()] = $ticket_name;
             $reg_csv_array[__("Datetimes of Ticket", "event_espresso")] = implode(", ", $datetimes_strings);
             //get datetime(s) of registration
             //add attendee columns
             foreach ($att_fields_to_include as $att_field_name) {
                 $field_obj = EEM_Attendee::instance()->field_settings_for($att_field_name);
                 if ($reg_row['Attendee_CPT.ID']) {
                     if ($att_field_name == 'STA_ID') {
                         $value = EEM_State::instance()->get_var(array(array('STA_ID' => $reg_row['Attendee_Meta.STA_ID'])), 'STA_name');
                     } elseif ($att_field_name == 'CNT_ISO') {
                         $value = EEM_Country::instance()->get_var(array(array('CNT_ISO' => $reg_row['Attendee_Meta.CNT_ISO'])), 'CNT_name');
                     } else {
                         $value = $this->_prepare_value_from_db_for_display(EEM_Attendee::instance(), $att_field_name, $reg_row[$field_obj->get_qualified_column()]);
                     }
                 } else {
                     $value = '';
                 }
                 $reg_csv_array[$this->_get_column_name_for_field($field_obj)] = $value;
             }
             //make sure each registration has the same questions in the same order
             foreach ($questions_for_these_regs_rows as $question_row) {
                 if (!isset($reg_csv_array[$question_row['Question.QST_admin_label']])) {
                     $reg_csv_array[$question_row['Question.QST_admin_label']] = null;
                 }
             }
             //now fill out the questions THEY answered
             foreach (EEM_Answer::instance()->get_all_wpdb_results(array(array('REG_ID' => $reg_row['Registration.REG_ID']), 'force_join' => array('Question'))) as $answer_row) {
                 /* @var $answer EE_Answer */
                 if ($answer_row['Question.QST_ID']) {
                     $question_label = $this->_prepare_value_from_db_for_display(EEM_Question::instance(), 'QST_admin_label', $answer_row['Question.QST_admin_label']);
                 } else {
                     $question_label = sprintf(__('Question $s', 'event_espresso'), $answer_row['Answer.QST_ID']);
                 }
                 if (isset($answer_row['Question.QST_type']) && $answer_row['Question.QST_type'] == EEM_Question::QST_type_state) {
                     $reg_csv_array[$question_label] = EEM_State::instance()->get_state_name_by_ID($answer_row['Answer.ANS_value']);
                 } else {
                     $reg_csv_array[$question_label] = $this->_prepare_value_from_db_for_display(EEM_Answer::instance(), 'ANS_value', $answer_row['Answer.ANS_value']);
                 }
             }
             $registrations_csv_ready_array[] = apply_filters('FHEE__EE_Export__report_registrations__reg_csv_array', $reg_csv_array, $reg_row);
         }
     }
     //if we couldn't export anything, we want to at least show the column headers
     if (empty($registrations_csv_ready_array)) {
         $reg_csv_array = array();
         $model_and_fields_to_include = array('Registration' => $reg_fields_to_include, 'Attendee' => $att_fields_to_include);
         foreach ($model_and_fields_to_include as $model_name => $field_list) {
             $model = EE_Registry::instance()->load_model($model_name);
             foreach ($field_list as $field_name) {
                 $field = $model->field_settings_for($field_name);
                 $reg_csv_array[$this->_get_column_name_for_field($field)] = null;
                 //$registration->get($field->get_name());
             }
         }
         $registrations_csv_ready_array[] = $reg_csv_array;
     }
     if ($event_id) {
         $event_slug = EEM_Event::instance()->get_var(array(array('EVT_ID' => $event_id)), 'EVT_slug');
         if (!$event_slug) {
             $event_slug = __('unknown', 'event_espresso');
         }
     } else {
         $event_slug = __('all', 'event_espresso');
     }
     $filename = sprintf("registrations-for-%s", $event_slug);
     $handle = $this->EE_CSV->begin_sending_csv($filename);
     $this->EE_CSV->write_data_array_to_csv($handle, $registrations_csv_ready_array);
     $this->EE_CSV->end_sending_csv($handle);
 }
 /**
  * delete_payment
  * 	delete a payment or refund made towards a transaction
  *
  * @access public
  *	@return void
  */
 public function delete_payment()
 {
     $json_response_data = array('return_data' => FALSE);
     $PAY_ID = isset($this->_req_data['delete_txn_admin_payment'], $this->_req_data['delete_txn_admin_payment']['PAY_ID']) ? absint($this->_req_data['delete_txn_admin_payment']['PAY_ID']) : 0;
     if ($PAY_ID) {
         $delete_txn_reg_status_change = isset($this->_req_data['delete_txn_reg_status_change']) ? $this->_req_data['delete_txn_reg_status_change'] : false;
         $payment = EEM_Payment::instance()->get_one_by_ID($PAY_ID);
         if ($payment instanceof EE_Payment) {
             $REG_IDs = $this->_get_existing_reg_payment_REG_IDs($payment);
             /** @type EE_Transaction_Payments $transaction_payments */
             $transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
             if ($transaction_payments->delete_payment_and_update_transaction($payment)) {
                 $json_response_data['return_data'] = $this->_build_payment_json_response($payment, $REG_IDs, $delete_txn_reg_status_change);
                 if ($delete_txn_reg_status_change) {
                     $this->_req_data['txn_reg_status_change'] = $delete_txn_reg_status_change;
                     //MAKE sure we also add the delete_txn_req_status_change to the
                     //$_REQUEST global because that's how messages will be looking for it.
                     $_REQUEST['txn_reg_status_change'] = $delete_txn_reg_status_change;
                     $this->_maybe_send_notifications();
                     $this->_process_registration_status_change($payment->transaction(), $REG_IDs);
                 }
             }
         } else {
             EE_Error::add_error(__('Valid Payment data could not be retrieved from the database.', 'event_espresso'), __FILE__, __FUNCTION__, __LINE__);
         }
     } else {
         EE_Error::add_error(__('A valid Payment ID was not received, therefore payment form data could not be loaded.', 'event_espresso'), __FILE__, __FUNCTION__, __LINE__);
     }
     $notices = EE_Error::get_notices(false, false, false);
     $this->_template_args = array('data' => $json_response_data, 'success' => $notices['success'], 'error' => $notices['errors'], 'attention' => $notices['attention']);
     $this->_return_json();
 }