/**
  * possibly toggles TXN status
  *
  * @param EE_Transaction $transaction
  * @param 	boolean $update_txn  	whether to save the TXN
  * @return 	boolean 	 	whether the TXN was saved
  * @throws \EE_Error
  */
 public function update_transaction_status_based_on_total_paid(EE_Transaction $transaction, $update_txn = TRUE)
 {
     // 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;
     }
     // set incoming TXN_Status
     $this->set_old_txn_status($transaction->status_ID());
     // set transaction status based on comparison of TXN_paid vs TXN_total
     if (EEH_Money::compare_floats($transaction->paid(), $transaction->total(), '>')) {
         $new_txn_status = EEM_Transaction::overpaid_status_code;
     } else {
         if (EEH_Money::compare_floats($transaction->paid(), $transaction->total())) {
             $new_txn_status = EEM_Transaction::complete_status_code;
         } else {
             if (EEH_Money::compare_floats($transaction->paid(), $transaction->total(), '<')) {
                 $new_txn_status = EEM_Transaction::incomplete_status_code;
             } else {
                 EE_Error::add_error(__('The total paid calculation for this transaction is inaccurate.', 'event_espresso'), __FILE__, __FUNCTION__, __LINE__);
                 return FALSE;
             }
         }
     }
     if ($new_txn_status !== $transaction->status_ID()) {
         // set incoming TXN_Status
         $this->set_new_txn_status($new_txn_status);
         $transaction->set_status($new_txn_status);
         if ($update_txn) {
             return $transaction->save() ? TRUE : FALSE;
         }
     }
     return FALSE;
 }
 /**
  * @param EEI_Payment $payment      the payment to process
  * @param array       $billing_info but should be empty for this gateway
  * @param string      $return_url   URL to send the user to after payment on the payment provider's website
  * @param string      $notify_url   URL to send the instant payment notification
  * @param string      $cancel_url   URL to send the user to after a cancelled payment attempt
  *                                  on the payment provider's website
  * @return EEI_Payment
  * @throws \EE_Error
  */
 public function set_redirection_info($payment, $billing_info = array(), $return_url = null, $notify_url = null, $cancel_url = null)
 {
     $redirect_args = array();
     $transaction = $payment->transaction();
     $primary_registrant = $transaction->primary_registration();
     $item_num = 1;
     /** @type EE_Line_Item $total_line_item */
     $total_line_item = $transaction->total_line_item();
     $total_discounts_to_cart_total = $transaction->paid();
     //only itemize the order if we're paying for the rest of the order's amount
     if (EEH_Money::compare_floats($payment->amount(), $transaction->total(), '==')) {
         $payment->update_extra_meta(EEG_Paypal_Standard::itemized_payment_option_name, true);
         //this payment is for the remaining transaction amount,
         //keep track of exactly how much the itemized order amount equals
         $itemized_sum = 0;
         $shipping_previously_added = 0;
         //so let's show all the line items
         foreach ($total_line_item->get_items() as $line_item) {
             if ($line_item instanceof EE_Line_Item) {
                 //it's some kind of discount
                 if ($line_item->total() < 0) {
                     $total_discounts_to_cart_total += abs($line_item->total());
                     $itemized_sum += $line_item->total();
                     continue;
                 }
                 //dont include shipping again.
                 if (strpos($line_item->code(), 'paypal_shipping_') === 0) {
                     $shipping_previously_added = $line_item->total();
                     continue;
                 }
                 $redirect_args['item_name_' . $item_num] = substr(sprintf(_x('%1$s for %2$s', 'Ticket for Event', 'event_espresso'), $line_item->name(), $line_item->ticket_event_name()), 0, 127);
                 $redirect_args['amount_' . $item_num] = $line_item->unit_price();
                 $redirect_args['quantity_' . $item_num] = $line_item->quantity();
                 //if we're not letting PayPal calculate shipping, tell them its 0
                 if (!$this->_paypal_shipping) {
                     $redirect_args['shipping_' . $item_num] = '0';
                     $redirect_args['shipping2_' . $item_num] = '0';
                 }
                 $item_num++;
                 $itemized_sum += $line_item->total();
             }
         }
         $taxes_li = $this->_line_item->get_taxes_subtotal($total_line_item);
         //ideally itemized sum equals the transaction total. but if not (which is weird)
         //and the itemized sum is LESS than the transaction total
         //add another line item
         //if the itemized sum is MORE than the transaction total,
         //add the difference it to the discounts
         $itemized_sum_diff_from_txn_total = round($transaction->total() - $itemized_sum - $taxes_li->total() - $shipping_previously_added, 2);
         if ($itemized_sum_diff_from_txn_total < 0) {
             //itemized sum is too big
             $total_discounts_to_cart_total += abs($itemized_sum_diff_from_txn_total);
         } elseif ($itemized_sum_diff_from_txn_total > 0) {
             $redirect_args['item_name_' . $item_num] = substr(__('Other charges', 'event_espresso'), 0, 127);
             $redirect_args['amount_' . $item_num] = $this->format_currency($itemized_sum_diff_from_txn_total);
             $redirect_args['quantity_' . $item_num] = 1;
             $item_num++;
         }
         if ($total_discounts_to_cart_total > 0) {
             $redirect_args['discount_amount_cart'] = $this->format_currency($total_discounts_to_cart_total);
         }
         //add our taxes to the order if we're NOT using PayPal's
         if (!$this->_paypal_taxes) {
             $redirect_args['tax_cart'] = $total_line_item->get_total_tax();
         }
     } else {
         $payment->update_extra_meta(EEG_Paypal_Standard::itemized_payment_option_name, false);
         //partial payment that's not for the remaining amount, so we can't send an itemized list
         $redirect_args['item_name_' . $item_num] = substr(sprintf(__('Payment of %1$s for %2$s', "event_espresso"), $payment->amount(), $primary_registrant->reg_code()), 0, 127);
         $redirect_args['amount_' . $item_num] = $payment->amount();
         $redirect_args['shipping_' . $item_num] = '0';
         $redirect_args['shipping2_' . $item_num] = '0';
         $redirect_args['tax_cart'] = '0';
         $item_num++;
     }
     if ($this->_debug_mode) {
         $redirect_args['item_name_' . $item_num] = 'DEBUG INFO (this item only added in sandbox mode';
         $redirect_args['amount_' . $item_num] = 0;
         $redirect_args['on0_' . $item_num] = 'NOTIFY URL';
         $redirect_args['os0_' . $item_num] = $notify_url;
         $redirect_args['on1_' . $item_num] = 'RETURN URL';
         $redirect_args['os1_' . $item_num] = $return_url;
         //			$redirect_args['option_index_' . $item_num] = 1; // <-- dunno if this is needed ?
         $redirect_args['shipping_' . $item_num] = '0';
         $redirect_args['shipping2_' . $item_num] = '0';
     }
     $redirect_args['business'] = $this->_paypal_id;
     $redirect_args['return'] = $return_url;
     $redirect_args['cancel_return'] = $cancel_url;
     $redirect_args['notify_url'] = $notify_url;
     $redirect_args['cmd'] = '_cart';
     $redirect_args['upload'] = 1;
     $redirect_args['currency_code'] = $payment->currency_code();
     $redirect_args['rm'] = 2;
     //makes the user return with method=POST
     if ($this->_image_url) {
         $redirect_args['image_url'] = $this->_image_url;
     }
     $redirect_args['no_shipping'] = $this->_shipping_details;
     $redirect_args['bn'] = 'EventEspresso_SP';
     //EE will blow up if you change this
     $redirect_args = apply_filters("FHEE__EEG_Paypal_Standard__set_redirection_info__arguments", $redirect_args, $this);
     $payment->set_redirect_url($this->_gateway_url);
     $payment->set_redirect_args($redirect_args);
     // log the results
     $this->log(array('message' => sprintf(__('PayPal payment request initiated.', 'event_espresso')), 'transaction' => $transaction->model_field_array()), $payment);
     return $payment;
 }
 /**
  * Returns TRUE or FALSE for whether or not this transaction cost any money
  *
  * @return boolean
  * @throws \EE_Error
  */
 public function is_free()
 {
     return EEH_Money::compare_floats($this->get('TXN_total'), 0, '==');
 }
 /**
  *    column_TXN_paid
  *
  * @param \EE_Transaction $item
  * @return mixed|string
  * @throws \EE_Error
  */
 public function column_TXN_paid(EE_Transaction $item)
 {
     $transaction_total = $item->get('TXN_total');
     $transaction_paid = $item->get('TXN_paid');
     if (\EEH_Money::compare_floats($transaction_total, 0, '>')) {
         // monies owing
         $span_class = 'txn-overview-part-payment-spn';
         if (\EEH_Money::compare_floats($transaction_paid, $transaction_total, '>=')) {
             // paid in full
             $span_class = 'txn-overview-full-payment-spn';
         } elseif (\EEH_Money::compare_floats($transaction_paid, 0, '==')) {
             // no payments made
             $span_class = 'txn-overview-no-payment-spn';
         }
     } else {
         $span_class = 'txn-overview-free-event-spn';
         $transaction_paid = 0;
     }
     $payment_method = $item->payment_method();
     $payment_method_name = $payment_method instanceof EE_Payment_Method ? $payment_method->admin_name() : __('Unknown', 'event_espresso');
     $content = '<span class="' . $span_class . ' txn-pad-rght">' . $transaction_paid !== 0 ? $item->get_pretty('TXN_paid') : $transaction_paid . '</span>';
     if ($transaction_paid > 0) {
         $content .= '<br><span class="ee-status-text-small">' . sprintf(__('...via %s', 'event_espresso'), $payment_method_name) . '</span>';
     }
     return $content;
 }