/**
  *
  * @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;
 }
 /**
  * Parser for the [PAYMENT_DUE_DATE_*] attribute type shortcode
  *
  * @since 4.8.28.rc.011
  *
  * @param string $shortcode  The shortcode being parsed.
  * @param EE_Transaction $transaction
  * @return string
  */
 protected function _get_payment_due_date($shortcode, EE_Transaction $transaction)
 {
     //if transaction is paid in full then we can just return an empty string
     if ($transaction->remaining() === 0) {
         return '';
     }
     $attrs = $this->_get_shortcode_attrs($shortcode);
     $format = isset($attrs['format']) ? $attrs['format'] : get_option('date_format');
     $days_until_due = isset($attrs['days_until_due']) ? (int) $attrs['days_until_due'] : 30;
     $prefix_text = isset($attrs['prefix_text']) ? $attrs['prefix_text'] : __('Payment in full due by: ', 'event_espresso');
     $transaction_created = $transaction->get_DateTime_object('TXN_timestamp');
     //setup date due:
     try {
         if ($transaction_created instanceof DateTime) {
             $date_due = $transaction_created->add(new DateInterval('P' . $days_until_due . 'D'))->format($format);
         } else {
             throw new Exception();
         }
     } catch (Exception $e) {
         //format was likely invalid.
         $date_due = 'Unable to calculate date due, likely the format string is invalid.';
     }
     return $prefix_text . $date_due;
 }